⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 012.txt

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 5 页
字号:

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 + -