WIN32汇编语言教程:第12章 多线程 · 12.4 线程间的同步(2)
.if ax == IDOK .if dwThreads or dwOption,F_STOP invoke KillTimer,hWnd,1 .else mov dwCounter1,0 mov dwCounter2,0 xor ebx,ebx .while ebx < 10 invoke CreateThread,NULL,0,\ offset _Counter,NULL,\ NULL,addr @dwThreadID invoke CloseHandle,eax inc ebx .endw invoke SetTimer,hWnd,1,500,NULL .endif .endif ;******************************************************************** .elseif eax == WM_CLOSE .if ! dwThreads invoke EndDialog,hWnd,NULL .endif ;******************************************************************** .elseif eax == WM_INITDIALOG push hWnd pop hWinMain invoke GetDlgItem,hWnd,IDOK mov hWinCount,eax ;******************************************************************** .else mov eax,FALSE ret .endif mov eax,TRUE ret _ProcDlgMain endp ;#################################################################### start: invoke GetModuleHandle,NULL mov hInstance,eax invoke DialogBoxParam,eax,DLG_MAIN,NULL,\ offset _ProcDlgMain,NULL invoke ExitProcess,NULL ;#################################################################### end start
目录中的ThreadSynErr.rc文件定义了如图12.3所示的界面。
图12.3 多线程同步的演示程序
ThreadSynErr.rc文件的代码为:
//################################################################## #include <resource.h> //################################################################## #define ICO_MAIN 1000 #define DLG_MAIN 1000 #define IDC_COUNTER1 1001 #define IDC_COUNTER2 1002 //################################################################## ICO_MAIN ICON "Main.ico" //################################################################## DLG_MAIN DIALOG 227, 187, 129, 56 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "多线程同步的演示程序" FONT 9, "宋体" { LTEXT "计数器一:", -1, 7, 7, 40, 8 LTEXT "计数器二:", -1, 7, 22, 41, 8 EDITTEXT IDC_COUNTER1, 51, 5, 71, 12, ES_READONLY | WS_BORDER | WS_TABSTOP EDITTEXT IDC_COUNTER2, 51, 20, 71, 12, ES_READONLY | WS_BORDER | WS_TABSTOP PUSHBUTTON "计数", IDOK, 72, 36, 50, 14 } //##################################################################
“问题程序”还是用循环计数的功能来演示,程序中设置了两个全局变量dwCounter1和dwCounter2用做计数器,当按下“计数”按钮的时候,程序将两个计数器清零,并且用循环语句建立10个线程来同时执行_Counter线程函数:
mov dwCounter1,0 mov dwCounter2,0 xor ebx,ebx .while ebx < 10 invoke CreateThread,NULL,0,offset _Counter,NULL,\ NULL,addr @dwThreadID invoke CloseHandle,eax inc ebx .endw invoke SetTimer,hWnd,1,500,NULL
最后,程序设立一个定时器来定时将计数器值显示到编辑框中。
在线程函数中,使用下面的计算代码:
.while ! (dwOption & F_STOP) inc dwCounter1 mov eax,dwCounter2 inc eax mov dwCounter2,eax .endw
在这段代码中,递增第一个计数器使用inc指令,由于计算用单条指令完成,所以计数器一不会因为同步问题出错,递增第二个计数器的代码使用了3条指令,首先将原来的计数值取到eax中,递增eax后再写回到变量中,如果不存在多个线程同步的问题,这两种算法的结果是一样的,显示到编辑框中的计数值应该是相等的。
在存在同步问题的情况下,如果线程在mov eax,dwCounter2或者inc eax指令执行后被打断,并且其他线程在这期间修改了dwCounter2的值的话,根据前面的分析,就会有一次计数值被丢失。如果显示到编辑框中的计数值不相等,则证明存在同步的问题,通过比较两个计数值的差值,还可以得知同步问题发生的机会是多少。
好了,大家可能都迫不及待地想看运行结果了吧,结果如图12.3所示,这是程序在笔者的450 MHz的计算机上运行了10秒以后的结果,可以看到,10个线程加起来总共进行了 783 189 430次计算,计数器二却丢失了783 189 430-274 090 739=509 098 691次计数,因为同步问题丢失的计数竟然占了65%,可见这绝对不是偶尔发生一次两次的事情,大家可以想像一下,如果有人往一个银行账户中汇款,三笔汇款中丢了两笔,人们会有何感想呢?