baronG 发表于 2010-11-2 12:07

每天三篇PaPer系列之一,多线程同步问题,学习笔记~~~

最近定下目标,坚持每天最少三篇paper~
- -
于是有就有了下文~~~
是关于对 多线程同步的一些理解,包括 事件,临界,信号灯,互斥体~
come on~

代码 来自 罗云彬 Win32asm 系列第12章~~
首先 [*]hInstance dd ?[*]hWinMain dd ?[*]hWinCount dd ?[*][*][*]dwCounter1 dd ?[*]dwCounter2 dd ?[*][*][*]dwThreads dd ? ;thread sum[*][*]hEvent dd ?[*][*]F_STOP equ 0001h[*]dwOption dd ?
复制代码
对每个变量做以下 解释(从上到下顺序)
1模块 handle
2 窗口 handle
3 按钮handle

4和5 用于 累加的var

6 线程数变量
7 事件 handle

8停止的flag
9 flag的var~~~

go~ [*]004011D4 syn_fix.<模块入口点>               6A 00                push 0[*]004011D6                                 E8 63000000          call <jmp.&kernel32.GetModuleHandleA>[*]004011DB                                 A3 00304000          mov dword ptr ds:,eax[*]004011E0                                 6A 00                push 0[*]004011E2                                 68 7F104000          push syn_fix.0040107F                   ; 消息处理过程[*]004011E7                                 6A 00                push 0[*]004011E9                                 68 E8030000          push 3E8[*]004011EE                                 50                   push eax[*]004011EF                                 E8 08000000          call <jmp.&user32.DialogBoxParamA>[*]004011F4                                 6A 00                push 0[*]004011F6                                 E8 3D000000          call <jmp.&kernel32.ExitProcess>[*][*]____分割线[*][*]0040107F                               /.55               push ebp[*]00401080                               |.8BEC               mov ebp,esp[*]00401082                               |.83C4 FC            add esp,-4[*]00401085                               |.53               push ebx[*]00401086                               |.57               push edi[*]00401087                               |.56               push esi[*]00401088                               |.8B45 0C            mov eax,dword ptr ss:[*]0040108B                               |.3D 13010000      cmp eax,113                           ;Switch (cases 10..113)[*]00401090                               |.75 4D            jnz short syn_fix.004010DF[*]00401092                               |.6A FF            push -1                                 ; /Timeout = INFINITE; Case 113 (WM_TIMER) of switch 0040108B[*]00401094                               |.FF35 18304000      push dword ptr ds:            ; |hObject = NULL[*]0040109A                               |.E8 AB010000      call <jmp.&kernel32.WaitForSingleObject>; \WaitForSingleObject[*]0040109F                               |.6A 00            push 0                                  ; /IsSigned = FALSE[*]....[*]...[*]..
复制代码
首先 我们先看 dlg初始化消息的处理~
源代码~


.elseif eax == WM_INITDIALOG
                              
                              push hWnd
                              pop hWinMain
                              invoke GetDlgItem,hWnd,IDC_BTN1
                              mov hWinCount,eax; 给我控制室的联系方式~~
                              
                              invoke CreateEvent,NULL,FALSE,TRUE,NULL;叫黑丝御姐去协调,那20多个工人的同步问题
                              ;要是一个工人干完了,直接联系下一个工人,同时通知第一个工人,到了就直接工作,谁也不用报告~
                              mov hEvent,eax      

1,保存 窗口句柄
2 获得按钮控件句柄
3 创建一直自动的事件,且为置位,这样第一个线程,在遇到WaitForSingleObject直接就可以开始,不会陷入等待,造成 无法执行下去的 bug~~~ [*]00401186                               |> \3D 10010000      cmp eax,110[*]0040118B                               |.75 2F            jnz short syn_fix.004011BC[*]0040118D                               |.FF75 08            push dword ptr ss:               ;Case 110 (WM_INITDIALOG) of switch 0040108B[*]00401190                               |.8F05 04304000      pop dword ptr ds:               ;得到窗口句柄存放到数据段[*]00401196                               |.68 EB030000      push 3EB                              ; /ControlID = 3EB (1003.)[*]0040119B                               |.FF75 08            push dword ptr ss:               ; |hWnd[*]0040119E                               |.E8 65000000      call <jmp.&user32.GetDlgItem>         ; \GetDlgItem[*]004011A3                               |.A3 08304000      mov dword ptr ds:,eax         ;获得 子控件句柄[*]004011A8                               |.6A 00            push 0                                  ; /EventName = NULL[*]004011AA                               |.6A 01            push 1                                  ; |InitiallySignaled = TRUE[*]004011AC                               |.6A 00            push 0                                  ; |ManualReset = FALSE[*]004011AE                               |.6A 00            push 0                                  ; |pSecurity = NULL[*]004011B0                               |.E8 77000000      call <jmp.&kernel32.CreateEventA>       ; \CreateEventA[*]004011B5                               |.A3 18304000      mov dword ptr ds:,eax         ;控件的句柄[*]004011BA                               |.EB 0C            jmp short syn_fix.004011C8
复制代码
接下来看~WM_COMMAND消息的处理 [*]004010E6                               |.8B45 10            mov eax,dword ptr ss:         ;Case 111 (WM_COMMAND) of switch 0040108B[*]004010E9                               |.66:3D EB03         cmp ax,3EB                              ;比较是否是相应的按钮控件[*]004010ED                               |.0F85 D5000000      jnz syn_fix.004011C8[*]004010F3                               |.833D 14304000 00   cmp dword ptr ds:,0[*];这里是通过判断dwThread来检测,是要开始计算,还是要停止,如果是已经开始计算了,要停止下来,[*];那么这个变量就是非零,反之亦然,如果是非零 就继续向下执行,非零就从0040110F开始执行了[*]004010FA                               |.74 13            je short syn_fix.0040110F[*]004010FC                               |.830D 1C304000 01   or dword ptr ds:,1[*]00401103                               |.6A 01            push 1                                  ; /TimerID = 1[*]00401105                               |.FF75 08            push dword ptr ss:               ; |hWnd[*]00401108                               |.E8 01010000      call <jmp.&user32.KillTimer>            ; \KillTimer[*]0040110D                               |.EB 4D            jmp short syn_fix.0040115C[*]0040110F                               |>C705 0C304000 0000>mov dword ptr ds:,0             ;计数变量清0[*]00401119                               |.C705 10304000 0000>mov dword ptr ds:,0[*]00401123                               |.33DB               xor ebx,ebx[*]00401125                               |.EB 1F            jmp short syn_fix.00401146[*]00401127                               |>8D45 FC            /lea eax,dword ptr ss:         ;线程ID变量的指针[*]0040112A                               |.50               |push eax                               ; /pThreadId[*]0040112B                               |.6A 00            |push 0                                 ; |CreationFlags = 0[*]0040112D                               |.6A 00            |push 0                                 ; |pThreadParm = NULL[*]0040112F                               |.68 00104000      |push syn_fix.00401000                  ; |ThreadFunction = syn_fix.00401000[*]00401134                               |.6A 00            |push 0                                 ; |StackSize = 0[*]00401136                               |.6A 00            |push 0                                 ; |pSecurity = NULL[*]00401138                               |.E8 F5000000      |call <jmp.&kernel32.CreateThread>      ; \CreateThread[*]0040113D                               |.FF75 FC            |push dword ptr ss:            ; /hObject[*]00401140                               |.E8 E1000000      |call <jmp.&kernel32.CloseHandle>       ; \CloseHandle[*]这里就开始执行线程了。我们到 线程函数里面 看看把~~~[*]这个是重点~
复制代码
第一条线程创建后 SetWindosText后~ [*]00401149                                       |.6A 00            push 0                                  ; /如果创建完20条线程以后~我们就创建一个定时器,用以将数值写到控件里面[*]0040114B                                       |.68 F4010000      push 1F4                              ; |Timeout = 500. ms[*]00401150                                       |.6A 01            push 1                                  ; |TimerID = 1[*]00401152                                       |.FF75 08            push dword ptr ss:               ; |hWnd[*]00401155                                       |.E8 BE000000      call <jmp.&user32.SetTimer>             ; \SetTimer[*][*]会创建一个定时器 500MS执行一次 WM_TIMER消息~,[*]0040108B                                       |.3D 13010000      cmp eax,113                                  ;Switch (cases 10..113)[*]00401090                                       |.75 4D            jnz short syn_fix.004010DF[*]00401092                                       |.6A FF            push -1                                    ; /Timeout = INFINITE; Case 113 (WM_TIMER) of switch[*]00401094                                       |.FF35 18304000      push dword ptr ds:                   ; |hObject = 00000064 (window)[*]0040109A                                       |.E8 A9010000      call <jmp.&kernel32.WaitForSingleObject>   ; \WaitForSingleObject[*];这里同样是用一组 WaitForSingleObject 来等待线程完成 ,才进行下一个线程[*]0040109F                                       |.6A 00            push 0                                       ; /IsSigned = FALSE[*]004010A1                                       |.FF35 0C304000      push dword ptr ds:                   ; |Value = A (10.)[*]004010A7                                       |.68 E9030000      push 3E9                                     ; |ControlID = 3E9 (1001.)[*]004010AC                                       |.FF35 04304000      push dword ptr ds:                   ; |hWnd = 00700340 ('同步问题解决',class='#32770')[*]004010B2                                       |.E8 5B010000      call <jmp.&user32.SetDlgItemInt>             ; \SetDlgItemInt[*]004010B7                                       |.6A 00            push 0                                       ; /IsSigned = FALSE[*]004010B9                                       |.FF35 10304000      push dword ptr ds:                   ; |Value = A (10.)[*]004010BF                                       |.68 EA030000      push 3EA                                     ; |ControlID = 3EA (1002.)[*]004010C4                                       |.FF35 04304000      push dword ptr ds:                   ; |hWnd = 00700340 ('同步问题解决',class='#32770')[*]004010CA                                       |.E8 43010000      call <jmp.&user32.SetDlgItemInt>             ; \SetDlgItemInt[*]004010CF                                       |.FF35 18304000      push dword ptr ds:                   ; /hEvent = 00000064 (window)[*]004010D5                                       |.E8 68010000      call <jmp.&kernel32.SetEvent>                ; \SetEvent[*];同样 告诉windows自己搞完了,让别的线程进~~~[*]004010DA                                       |.E9 E7000000      jmp syn_fix.004011C6
复制代码
[*]00401025                                       |> /6A FF            /push -1                                     ; /Timeout = INFINITE[*]00401027                                       |. |FF35 18304000      |push dword ptr ds:                  ; |hObject = 00000064 (window)[*]0040102D                                       |. |E8 16020000      |call <jmp.&kernel32.WaitForSingleObject>    ; \WaitForSingleObject[*];这里WaitForSingleObject来判断下一个线程是否能运行,当然因为我们创建事件的时候是置位的,所以第一条线程顺利运行.....其他的事件就会等待在这里[*]00401032                                       |. |FF05 0C304000      |inc dword ptr ds:[*]00401043                                       |.FF35 18304000      |push dword ptr ds:                  ; /hEvent = 00000064 (window)[*]00401049                                       |.E8 F4010000      |call <jmp.&kernel32.SetEvent>               ; \SetEvent[*][*];等到线程要运行的执行运行完,我们就使用SetEvent置位,相当于告诉windows,我做完了,让别的线程进来 XXOO把~~~~
复制代码
OK事件搞完了~~~
我们看一下 临界区~~~
[*]临界区实现的是互斥技术。在任何时刻,只有一个线程能进入临界区。只有在该线程离开临界区后,其它线程才能进入。临界区对象不能被移动或者拷贝,进程也不能够修改该对象,而必须把它看作逻辑上不透明的(小黑箱)。使用由Win32 API提供的临界区函数来管理临界区对象。[*][*]    要使用临界区,首先需要定义一个临界区对象,此时,进程负责分配相应内存:[*]      CRITICAL_SECTION cs;[*][*]    然后,临界区对象必须由程序中某个线程初始化:[*]      InitializeCriticalSection(&cs);[*][*]    当初始化完该临界区对象后,线程可以调用下面的函数进入临界区:[*]      EnterCriticalSection (&cs);[*][*]    进入临界区后,该线程被认为拥有该入临界区对象。因此,其它线程调用EnterCriticalSection想进入该入临界区时,会被挂起。赶到拥有临界区对象的线程离开临界区后,它才有机会获得该临界区对象。一旦一个线程拥有临界区,那么它可以再调用EnterCriticalSection或者TryEnterCriticalSection而不会阻塞它自己的执行。这防止了一个线程因等待它自己所拥有的临界区而出现死锁。(TryEnterCriticalSection与TryEnterCriticalSection的区别:前者无论是否获得临界区,都立即返回;而后者在获得临界区前一直处理阻塞状态。TryEnterCriticalSection可以通过函数的返回值判断是否获得临界区,如果没有,可以做其它事情,而不是处于等待状态。)[*][*]    离开临界区:[*]      LeaveCriticalSection(&cs);[*][*]    当临界区不再需要时,可将其删除,从而释放相应系统资源:[*]      DeleteCriticalSection (&cs);[*][*]    使用临界区有一个限制,它只能用于同一进程内的线程间的互斥。而在某些情况下,需要协调两个不同进程对同一资源的共享(如共享内存)。此时,已不能再使用临界区做到,需要借助于互斥对象(mutex object)来实现(注:mutex是一个合成词)。
复制代码

理论我是从 网上直接粘贴的 - - 大家直接看把~~~
就是定义了 一个 CRITIGAL_SECTION 结构
然后初始化,然后 EnterCriticalSection 一下,这样 就保证了 只有一条线程可以操作 指令,剩下的执行到这里 就被挂起了。 嘿嘿


还是看OD~~ [*]线程[*]标识       入口       数据块       最后错误            状态      优先权   用户时间      系统时间[*]0000009C   7C810856   7FF99000   ERROR_SUCCESS (000激活         32 + 0       1.1875 s      0.0468 s[*]000000F8   7C810856   7FFD3000   ERROR_SUCCESS (000激活         32 + 0       0.0937 s      0.4531 s[*]00000344   7C810856   7FFD5000   ERROR_SUCCESS (000激活         32 + 0       0.0937 s      0.6718 s[*]0000055C   7C810856   7FF9B000   ERROR_SUCCESS (000激活         32 + 0       1.4218 s      0.0781 s[*]00000560   7C810856   7FFDC000   ERROR_SUCCESS (000激活         32 + 0       0.0312 s      0.5000 s[*]00000654   7C810856   7FFDA000   ERROR_SUCCESS (000激活         32 + 0       0.0312 s      0.4218 s[*]0000071C   7C810856   7FFDB000   ERROR_SUCCESS (000激活         32 + 0       0.7968 s      0.0937 s[*]00000748   7C810856   7FFD4000   ERROR_SUCCESS (000激活         32 + 0       1.0625 s      0.0625 s[*]00000908(004011C1   7FFDF000   ERROR_ACCESS_DENIE激活         32 + 0       0.0468 s      0.1093 s[*]000009A8   7C810856   7FF9A000   ERROR_SUCCESS (000激活         32 + 0       0.1093 s      0.9218 s[*]00000A24   7C810856   7FF9D000   ERROR_SUCCESS (000激活         32 + 0       0.9062 s      0.1093 s[*]00000AF0   7C810856   7FFD9000   ERROR_SUCCESS (000激活         32 + 0       1.1562 s      0.0468 s[*]00000B00   7C810856   7FFD7000   ERROR_SUCCESS (000激活         32 + 0       1.0937 s      0.0312 s[*]00000BA4   7C810856   7FF9C000   ERROR_SUCCESS (000激活         32 + 0       0.1093 s      0.4531 s[*]00000CE8   7C810856   7FFD8000   ERROR_SUCCESS (000激活         32 + 0       0.1406 s      0.7031 s[*]00000D10   7C810856   7FF9E000   ERROR_SUCCESS (000激活         32 + 0       0.1406 s      0.6093 s[*]00000D2C   7C810856   7FFDD000   ERROR_SUCCESS (000激活         32 + 0       1.5468 s      0.0781 s[*]00000E20   7C810856   7FF98000   ERROR_SUCCESS (000激活         32 + 0       0.0937 s      0.4531 s[*]00000E44   7C810856   7FFDE000   ERROR_SUCCESS (000激活         32 + 0       0.0625 s      0.5000 s[*]00000F1C   7C810856   7FF97000   ERROR_SUCCESS (000激活         32 + 0       1.1250 s      0.0625 s[*]00000F88   7C810856   7FF9F000   ERROR_SUCCESS (000激活         32 + 0       1.2187 s      0.0781 s
复制代码
这里可以看到 所有线程还都是激活的 ,当我们运行 EnterCriticalsection后 我们看

线程
标识       入口       数据块       最后错误            状态      优先权   用户时间      系统时间
0000009C   7C810856   7FF99000   ERROR_SUCCESS (000暂停         32 + 0       1.1875 s      0.0468 s
000000F8   7C810856   7FFD3000   ERROR_SUCCESS (000暂停         32 + 0       0.0937 s      0.4531 s
00000344   7C810856   7FFD5000   ERROR_SUCCESS (000暂停         32 + 0       0.0937 s      0.6718 s
0000055C   7C810856   7FF9B000   ERROR_SUCCESS (000暂停         32 + 0       1.4218 s      0.0781 s
00000560   7C810856   7FFDC000   ERROR_SUCCESS (000暂停         32 + 0       0.0312 s      0.5000 s
00000654   7C810856   7FFDA000   ERROR_SUCCESS (000暂停         32 + 0       0.0312 s      0.4218 s
0000071C   7C810856   7FFDB000   ERROR_SUCCESS (000暂停         32 + 0       0.7968 s      0.0937 s
00000748   7C810856   7FFD4000   ERROR_SUCCESS (000暂停         32 + 0       1.0625 s      0.0625 s
00000908(004011C1   7FFDF000   ERROR_ACCESS_DENIE暂停         32 + 0       0.0468 s      0.1093 s
000009A8   7C810856   7FF9A000   ERROR_SUCCESS (000暂停         32 + 0       0.1093 s      0.9375 s
00000A24   7C810856   7FF9D000   ERROR_SUCCESS (000暂停         32 + 0       0.9062 s      0.1093 s
00000AF0   7C810856   7FFD9000   ERROR_SUCCESS (000暂停         32 + 0       1.1562 s      0.0468 s
00000B00   7C810856   7FFD7000   ERROR_SUCCESS (000暂停         32 + 0       1.0937 s      0.0312 s
00000BA4   7C810856   7FF9C000   ERROR_SUCCESS (000暂停         32 + 0       0.1093 s      0.4531 s
00000CE8   7C810856   7FFD8000   ERROR_SUCCESS (000暂停         32 + 0       0.1406 s      0.7031 s
00000D10   7C810856   7FF9E000   ERROR_SUCCESS (000暂停         32 + 0       0.1406 s      0.6093 s
00000D2C   7C810856   7FFDD000   ERROR_SUCCESS (000暂停         32 + 0       1.5468 s      0.0781 s
00000E20   7C810856   7FF98000   ERROR_SUCCESS (000暂停         32 + 0       0.0937 s      0.4531 s
00000E44   7C810856   7FFDE000   ERROR_SUCCESS (000激活         32 + 0       0.0625 s      0.5000 s
00000F1C   7C810856   7FF97000   ERROR_SUCCESS (000暂停         32 + 0       1.1250 s      0.0625 s
00000F88   7C810856   7FF9F000   ERROR_SUCCESS (000暂停         32 + 0       1.2187 s      0.0781 s
只有一条线程运行的

执行 LeaveCriticalsection后~~

线程
标识       入口       数据块       最后错误            状态      优先权   用户时间      系统时间
000002A0   7C810856   7FFDE000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
000002E8   7C810856   7FFD6000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
000005A8   7C810856   7FFD9000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
000007E4   7C810856   7FFD4000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000854(004011C1   7FFDF000   ERROR_SUCCESS (000激活         32 + 0       0.0625 s      0.1875 s
0000086C   7C810856   7FFDA000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000880   7C810856   7FF97000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000890   7C810856   7FFD3000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000904   7C810856   7FF9E000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
0000095C   7C810856   7FFD5000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000AC8   7C810856   7FF9B000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000B54   7C810856   7FFD8000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000B64   7C810856   7FF9D000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000B84   7C810856   7FF99000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000D44   7C810856   7FF9C000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000DAC   7C810856   7FFDC000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000E88   7C810856   7FF9F000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000EF0   7C810856   7FFD7000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000F6C   7C810856   7FF98000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s
00000F84   7C810856   7FFDD000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0156 s
00000F8C   7C810856   7FF9A000   ERROR_SUCCESS (000激活         32 + 0       0.0000 s      0.0000 s

当再次有任何一条 线程执行到Enter的时候~~~ ,enter就会把剩下的线程,都挂起。,保证了同步~~~



我们再来看 互斥体
[*]windows api中提供了一个互斥体,功能上要比临界区强大。[*]Mutex是互斥体的意思,当一个线程持有一个Mutex时,其它线程申请持有同一个Mutex会被阻塞,[*]因此可以通过Mutex来保证对某一资源的互斥访问(即同一时间最多只有一个线程访问)。[*]调用CreateMutex可以创建或打开一个Mutex对象,[*]HANDLE CreateMutex([*]LPSECURITY_ATTRIBUTES lpMutexAttributes,[*]BOOL bInitialOwner,[*]LPCTSTR lpName[*]);[*][*]其中参数lpMutexAttributes用来设定Mutex对象的安全描述符和是否允许子进程继承句柄。[*]bInitialOwner表明是否将Mutex的持有者设置为调用线程。[*]lpName参数设置Mutex的名字,该名字区分大小写并不能包含"",最大长度为MAX_PATH,可设置为NULL表明该Mutex为匿名对象。[*]如果调用成功,则返回Mutex的句柄,否则返回NULL,如果lpName不为NULL且调用前同名的Mutex已被创建,则返回同名Mutex的句柄,此时调用GetLastError将返回ERROR_ALREADY_EXISTS,参数bInitialOwner将被忽略。
复制代码

理论大家自己 去 msdn google baidu ...
- -
我们再使用Mutext的时候 要使用 先获取互斥量,
获取互斥量 我们使用WaitForSingleObject
当互斥量被等待成功,他将自动复位

其他线程等待
,线程使用完后,调用
ReleaseMutex,hMutex
释放后,互斥量自动置位,此时 其他线程可以进入~~~



- - 就是 代码写的不一样
CreateMutex
WaitForSingleObject
ReleaseMutex
- -
很简单~~


——————
信号灯有点不一样~~


[*]Semaphore是旗语的意思,在Windows中,Semaphore对象用来控制对资源的并发访问数。Semaphore对象具有一个计数值,当值大于0时,Semaphore被置信号,当计数值等于0时,Semaphore被清除信号。每次针对Semaphore的wait functions返回时,计数值被减1,调用ReleaseSemaphore可以将计数值增加 lReleaseCount 参数值指定的值。[*][*]CreateSemaphore函数用于创建一个Semaphore[*][*]HANDLE CreateSemaphore([*]LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,[*]LONG lInitialCount, 这个就是指示创建后,信号灯对象的计数值,这里我们只需要一个就设置为1[*]LONG lMaximumCount, ;这个是最多,多少个线程能获取这个 计数值[*]LPCTSTR lpName[*]);[*][*]lpSemaphoreAttributes为安全属性,[*]lInitialCount为Semaphore的初始值,[*]lMaximumCount为最大值,[*]lpName为Semaphore对象的名字,NULL表示创建匿名Semaphore[*][*]此外还可以调用OpenSemaphore来打开已经创建的非匿名Semaphore[*][*]HANDLE OpenSemaphore([*]DWORD dwDesiredAccess,[*]BOOL bInheritHandle,[*]LPCTSTR lpName[*]);[*][*]调用ReleaseSemaphore增加Semaphore计算值[*][*]BOOL ReleaseSemaphore([*]HANDLE hSemaphore,[*]LONG lReleaseCount,[*]LPLONG lpPreviousCount[*]);[*][*]lpReleaseCount参数表示要增加的数值,[*]lpPreviousCount参数用于返回之前的计算值,如果不需要可以设置为NULL
复制代码



当然还是用 WaitForSingleObject 等待到信号灯后,会把信号灯计数-1,这里我们 直接
创建的时候,初始化值,和最大值 都设置为1 ,那么 一个线程进入后,计数值就会-1 == 0,那么 状态变成 复位,其他的线程就等待这里了
执行到最后 调用
ReleaseSemaphore函数
ReleaseSemaphore 函数会把计数值 +1
那么 对象状态又变成置位,那么,其他线程就可进入执行了~~~
- - 如此反复。。

写完了~~~..

笔记只是,一点愚见,
错误的地方,还请各位多多批评和指出
欢迎指导

然后我说完了~~~

czjh2008 发表于 2010-11-2 13:43

我最近也在学这个,不错!
页: [1]
查看完整版本: 每天三篇PaPer系列之一,多线程同步问题,学习笔记~~~