📄 012.txt
字号:
hWinCount dd ?
hWinPause dd ?
hEvent dd ? ;事件对象句柄
dwOption dd ?
F_PAUSE equ 0001h
F_STOP equ 0002h
F_COUNTING equ 0004h
.const
szStop db ~停止计数~,0
szStart db ~计数~,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Counter proc uses ebx esi edi,_lParam
or dwOption,F_COUNTING
and dwOption,not (F_STOP or F_PAUSE)
invoke SetEvent,hEvent
invoke SetWindowText,hWinCount,addr szStop
invoke EnableWindow,hWinPause,TRUE
xor ebx,ebx
.while ! (dwOption & F_STOP)
inc ebx
invoke SetDlgItemInt,hWinMain,IDC_COUNTER,ebx,FALSE
invoke WaitForSingleObject,hEvent,INFINITE
.endw
invoke SetWindowText,hWinCount,addr szStart
invoke EnableWindow,hWinPause,FALSE
and dwOption,not (F_COUNTING or F_STOP or F_PAUSE)
ret
_Counter endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
local @dwThreadID
mov eax,wMsg
;********************************************************************
.if eax == WM_COMMAND
mov eax,wParam
.if ax == IDOK
.if dwOption & F_COUNTING
invoke SetEvent,hEvent
or dwOption,F_STOP
.else
invoke CreateThread,NULL,0,\
offset _Counter,NULL,\
NULL,addr @dwThreadID
invoke CloseHandle,eax
.endif
.elseif ax == IDC_PAUSE
xor dwOption,F_PAUSE
.if dwOption & F_PAUSE
invoke ResetEvent,hEvent
.else
invoke SetEvent,hEvent
.endif
.endif
;********************************************************************
.elseif eax == WM_CLOSE
invoke CloseHandle,hEvent
invoke EndDialog,hWnd,NULL
;********************************************************************
.elseif eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke GetDlgItem,hWnd,IDOK
mov hWinCount,eax
invoke GetDlgItem,hWnd,IDC_PAUSE
mov hWinPause,eax
invoke CreateEvent,NULL,TRUE,FALSE,NULL
mov hEvent,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
修改以后编译执行,然后打开任务管理器观察程序的CPU占用率就可以发现,当按下 “暂停/恢复”按钮暂停计数后,程序的CPU占用率马上下降到接近零,再次按动按钮恢复计数时,CPU占用率会恢复到原来的数值,这说明程序的运行效率得到了很大的提高。
12.4.1 产生同步问题的原因
对于多线程的程序来说,线程之间的同步永远是个重要的问题。如果多个线程都要存取同样的对象(如存取同样的内存变量或读写同一个文件等),而一个线程操作的结果反过来又会影响另一个线程的运行的时候,同步问题就变得异常重要。
产生同步问题的根源在于线程之间的切换是无法预测的,一个线程无法知道什么时候自己的时间片会结束,也无法知道下一个时间片会被分配给哪个线程。事实上,线程可以在任何地方被Windows打断。读者应该记住的是:惟一可以确定的事实就是线程只能在两条指令之间被打断,因为指令是CPU执行的最小单位,线程不可能在一条指令执行到一半的时候被打断。
对于单线程的程序来说,主线程在单个时间片结束的时候被Windows挂起,然后在轮到下一个时间片的时候继续执行,这中间整个进程的环境不会有任何改变,因为进程中不存在其他线程。但对于多线程的程序来说,在主线程挂起的过程中可能会有子线程被分配了时间片,如果子线程在执行中改变了主线程正在存取的对象,就可能会引发错误的结果。举一个例子来说明,假定往银行账户里存钱的操作步骤有3步:
(1)获取账户的余额。
(2)将用户存入的数额和余额相加,得到新的余额。
(3)将账户中的余额数据更新为新的数值。
现在从两个不同的储蓄所里同时向一个账户存钱,假设原来的余额是10 000元,储蓄所A要存入1 000元,储蓄所B要存入2 000元,如果没有同步机制,就可能发生下面的情况:
① 蓄所A首先执行第(1)步,获得余额数据10 000,然后进行第(2)步运算得到结果11 000元。
② 这时储蓄所B的业务也刚好发生,在储蓄所A计算第(2)步的过程中,储蓄所B执行了第(1)步,由于储蓄所A还没有执行到第(3)步,所以账户余额还没有被更新,储蓄所B得到的余额数据还是10 000元。
③ 储蓄所B计算新余额,得到结果12 000元。
④ 在储蓄所B计算新余额的过程中,储蓄所A执行了第(3)步,将余额更新为11 000元。
⑤ 最后,储蓄所B执行了第(3)步,将自己的计算结果12 000元更新到余额数据中。
结果就是储蓄所A的业务实际上是丢失了;另一种情况,假如储蓄所B的动作很快,在上面的第④步骤发生之前,在第③步骤中就将计算结果12 000更新到余额数据中了,那么在第④步骤中储蓄所A的计算结果11 000就会将12 000覆盖,这时的结果就是储蓄所B的业务丢失了,所以同步问题产生的错误结果是很难预测的。
将这个比喻引伸到两个线程的同步问题上就表现在:假如线程A将某个内存变量的值取到eax寄存器中,准备在经过运算后将结果写回去,这时被Windows切换到线程B中,线程B在这个时间片中对同一个内存变量进行了修改,当切换回线程A的时候,线程A在上一个时间片中刚取到eax中的数值和内存变量中的值就不同步了,计算结果当然就是错误的。
有人可能会认为出现这种情况的概率是很低的,线程中有这么多条指令要执行,难道偏偏就在程序取完数据还没开始处理的时候被系统打断吗?通过下面的例子就可以发现发生这种事情的可能性有多大。例子程序位于所附光盘的Chapter12\ThreadSynErr目录中,还是用递增计数器的方法来演示同步问题,ThreadSynErr.asm的内容如下:
.386
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDC_COUNTER1 equ 1001
IDC_COUNTER2 equ 1002
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinMain dd ?
hWinCount dd ?
dwThreads dd ?
dwOption dd ?
F_STOP equ 0001h
dwCounter1 dd ?
dwCounter2 dd ?
.const
szStop db ~停止计数~,0
szStart db ~计数~,0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -