同步问题 Event的使用

来源:互联网 发布:凡人修仙传 知乎 编辑:程序博客网 时间:2024/05/21 20:58

如果,共用的记体同时有两个Process去做写入的动作,那结果很难想像会如何,所以这
便得使用同步的技巧,现在介绍的是最简单的一种-->Event。
它的做法是:
   1.於系统产生一个Event物件,物件名称相同的话,会指向同一个物件,所以想要有
     两个Event物件,便要有两个不同的名称(这名称以字串来存)。
     Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" _
     (lpEventAttributes As Any, ByVal bManualReset As Long, _
      ByVal bInitialState As Long, ByVal lpName As String) As Long
     一般是用以下的方式呼叫,以产生一个Event物件,并传回hEvent。其中前二个叁数
     的使用请查Win32 help,一般我们如此用便能Work了。第三个叁数传0代表Event物
     件开始时是处於UnSignal的状态,若是1则表Signal

     hEvent = CreateEvent(ByVal 0, 1, 0, "MyEvent")

   2.以WaitForSingleObject() Check Event物件是处於什麽状态,若是Signal,则立
     即结束WaitForSingleObject()指令,而接着执行下一个指令。如果处於UnSignal
     状态,则WaitForSingleObject()指令会一直等待,直到有Thread将该Event物件的
     状态变成Signal或TimeOut才会再执行下一行指令。

     i = WaitForSingleObject(hEvent, 5000) '等5秒,若没有Signal则TimeOut执行下一行指令
     Label1.Caption = i

   3.以 SetEvent(hEvent) 来Signal Event
    ResetEvent(hEvent)来UnSignal Event
    PulseEvent(hEvent) 来Signale Event後之即UnSignal Event物件
   4.CloseHandle(hEvent)来Close Event物件

这样应还是有些模糊,换个角度来说,这好比红绿灯的做法,CreateEvent便是架一个
红绿灯,所谓处於Signal状态,便是处於绿灯的情况,任何Thread可在执行WaitForSingleObject
後,而立即通行(好比看看目前的灯号是绿灯,可通行),而处於UnSignal状态便是红灯,
不可通行,红 灯时程式如有执行WaitForSingleObject()则程式会停在该行,直到绿灯或Time Out
当然,通不通行一定要使用WaitForSingleObject先来判定,如果设了UnSignal的状态
,而没有WaitForSingleObject,就好比红绿灯仅叁考,而不管它。

以底下的个Process来看,假设ProcessA先执行,执行一半时,Process B介入,Process A
做某些启始化的动作,要做完後给Process B来读取。就算Process B先做,因一开始Event Object
是处於UnSignal状态,所以它在WaitForSingleObject那一行会一直等,直到Process A
执行到SetEvent()指令,请注意,虽Event物件处於UnSignal,但Process A没有用WaitforsingleObject
指令,也就是它没看红绿灯,直接过去了,等它过去後才设绿灯给Process B看。

Process A
   hEvent = CreateEvent(ByVal 0, 1, 0, "MyEvent")
   '做起始化动作,可能花一些时问,还没有完成方时,Process B起来了

   Call SetEvent(hEvent) '设定绿灯

Process B
  hEvent = CreateEvent(ByVal 0, 1, 0, "MyEvent")
  i = WaitForSingleObject(hEvent, INFINITE) 'INFINITE=&HFFFF 表无限等待
  '做读取动作

另有一件要提出说明的,现在我们都以Process看,事实上应以Thread来看,上面的例子
将Process A --> Thread A,Process B --> Thread B会更好,但VB难做MultiThread,
故一个Process是由一个Thread来做,所以我上面的例子是以Process来看。

而Event做上面这种例子最好,这Event只是一个大家共用的物件,任何人都可以设定它
的灯号,也就是说,Process A设定它为UnSignal时,Process B可设它为Signal,并不一定
要由Process A来做,而且在设定成Signal後,WaitForSingleObject的执行结束,不会
自动设定为UnSignal,所以,只要是Signal,所有的Thread都可以通行,这隐含一个问题
,以共用记意体的例子来说,如果在Signal的情况下,有两个Process同时要做写的动作
,虽WaitForSingleObejct执行後,立即ResetEvent,但分成多个指令来做,有可能RestEvent
还没有做,另一个Process紧接着做完WaitforSingleObejct而得知是Signal,如此一来,
就有可能会有问题,这时候要使用Mutex,不能使用Event,Mutex能确保同一时间内,只有
一个Thread在做某件事,Event不行,它只能做简单的灯号通行控制。

注:当处於unsigal时即使是同一个Thread使用WaitforsingleObject()同样会造成等待
    的情况

以下提供一个Class来做Event的控制,这程式最好同时有两个执行个体,比较能显示作用
方法:
   Create(字串)  产生一Event物件
   Signal UnSignal PulseSignal      设定 Event的状态
   ChkSignal  Check Event的状态 1: signal   0:因unsignal造成TimeOut
   DelEvent   Close Event
属性
   TimeOut 设定ChkSignal Time Out的时间,单位千分之一秒

'以下在TEvent.Cls
Option Explicit
Const INFINITE = &HFFFF
Const ERROR_ALREADY_EXISTS = 183&
Const WAIT_TIMEOUT = &H102

Private Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" _(lpEventAttributes As Any, ByVal bManualReset As Long, _ByVal bInitialState As Long, ByVal lpName As String) As Long
Private Declare Function SetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
Private Declare Function ResetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
Private Declare Function PulseEvent Lib "kernel32" (ByVal hEvent As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, _ByVal dwMilliseconds As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private hEvent As Long
Private m_TimeOut As Long
Public Function Create(ByVal EventName As String) As Long
If EventName = "" Then
   Create = -1
   Exit Function
End If
If hEvent <> 0 Then
   Create = 0
   Exit Function
End If
Dim i As Long
hEvent = CreateEvent(ByVal 0, 1, 0, EventName)
If i = 0 Then
   Create = 1
End If
End Function
Public Sub DelEvent()
   Call CloseHandle(hEvent)
   hEvent = 0
End Sub

Public Sub Signal()
Call SetEvent(hEvent)
End Sub
Public Sub PulseSignal()
Call PulseEvent(hEvent)
End Sub
Public Sub UnSignal()
Call ResetEvent(hEvent)
End Sub
Public Function ChkSignaled()
If hEvent = 0 Then
   ChkSignaled = -1
   Exit Function
End If
Dim i As Long
i = WaitForSingleObject(hEvent, m_TimeOut)
If i = 0 Then
   ChkSignaled = 1
Else
   If i = WAIT_TIMEOUT Then
      ChkSignaled = 0
   Else
      ChkSignaled = -1
   End If
End If
End Function

Public Property Get TimeOut() As Long
TimeOut = m_TimeOut
End Property

Public Property Let TimeOut(ByVal vNewValue As Long)
If vNewValue < 0 Then
   vNewValue = 0
End If
m_TimeOut = vNewValue
End Property

Private Sub Class_Initialize()
m_TimeOut = INFINITE
End Sub

Private Sub Class_Terminate()
Call DelEvent
End Sub


'以下在Form 需3个Command Button 一个label
Option Explicit
Dim aa As New TEvent

Private Sub Command1_Click()
aa.Signal
End Sub

Private Sub Command2_Click()
aa.UnSignal
End Sub

Private Sub Command3_Click()
Dim i As Long
aa.TimeOut = 5000
i = aa.ChkSignaled
Label1.Caption = "等待中"
DoEvents
If i = 1 Then
   Label1.Caption = "绿灯了"
Else
   Label1.Caption = "Time Out了"
End If
End Sub

Private Sub Form_Load()
aa.Create ("MyEvent")
End Sub