2011-12-19 75 views
2

我有一个vb6多线程应用程序工作,我想使用互斥锁来保护数据。预期的行为是,当一个线程试图获得对现有互斥锁的锁定时,当调用“WaitForSingleObject”函数时,该线程将阻塞,直到发出互斥信号。我所遇到的是整个应用程序冻结。互斥锁使用多线程时冻结整个应用程序

要复制我的项目,请打开VB6并创建一个新的Active X EXE。使用默认名称创建一个模块。将此代码放置在它:

Option Explicit 

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long 

Sub Main() 
    ' this hack is necessary to ensure that we only 'create' the application window once.. 
    Dim hwnd As Long 
    hwnd = FindWindow(vbNullString, "Form1") 
    If hwnd = 0 Then 
     Dim f As Form1 
     Set f = New Form1 
     f.Show 
     Set f = Nothing 
    End If 
End Sub 

接下来,创建一个类,使用默认名称和该代码添加到它:

Option Explicit 

Private Const INFINITE = -1& 
Private Const STANDARD_RIGHTS_REQUIRED As Long = &HF0000 
Private Const SYNCHRONIZE As Long = &H100000 
Private Const MUTANT_QUERY_STATE As Long = &H1 
Private Const MUTANT_ALL_ACCESS As Long = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or MUTANT_QUERY_STATE) 

Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long 
Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" (lpMutexAttributes As Any, ByVal bInitialOwner As Long, ByVal lpName As String) As Long 
Private Declare Function OpenMutex Lib "kernel32" Alias "OpenMutexA" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal lpName As String) As Long 
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long 
Private Declare Function ReleaseMutex Lib "kernel32" (ByVal hMutex As Long) As Long 

Private Const MUTEX_NAME As String = "mymutex" 
Private m_hCurrentMutex As Long 

Public Sub Class_Terminate() 
    Call ReleaseIt 
End Sub 

Public Sub LockIt(success As String) 
    Dim hMutex   As Long 

    MsgBox "Lockit t:" & App.ThreadID 
    hMutex = OpenMutex(STANDARD_RIGHTS_REQUIRED, 0, MUTEX_NAME) 
    If hMutex <> 0 Then 
     Form1.Caption = "waiting on mutex" 
     MsgBox "waiting t:" & App.ThreadID 
     Dim res As Long 
     Do 
      'MsgWaitForMultipleObjects 
      res = WaitForSingleObject(hMutex, INFINITE) 
      DoEvents 
     Loop While res = -1 
     m_hCurrentMutex = hMutex 
    Else 
     Form1.Caption = "creating mutex" 
     m_hCurrentMutex = CreateMutex(ByVal 0&, 1, MUTEX_NAME) 
    End If 
    Form1.Caption = success 
    MsgBox success 
End Sub 

Public Sub ReleaseIt() 
    If m_hCurrentMutex <> 0 Then 
     Call ReleaseMutex(m_hCurrentMutex) 
     Call CloseHandle(m_hCurrentMutex) 
     m_hCurrentMutex = 0 
    End If 
End Sub 

最后,在主窗体上添加4个命令按钮和下面的代码:

Option Explicit 
Dim c(1) As Class1 

'Lock 
Private Sub Command1_Click() 
    If c(0) Is Nothing Then Set c(0) = CreateObject("Project1.Class1") 
    Call c(0).LockIt("Object0") 
End Sub 
Private Sub Command2_Click() 
    If c(1) Is Nothing Then Set c(1) = CreateObject("Project1.Class1") 
    Call c(1).LockIt("Object1") 
End Sub 


'Free 
Private Sub Command3_Click() 
    If c(0) Is Nothing Then Set c(0) = CreateObject("Project1.Class1") 
    Call c(0).ReleaseIt 
End Sub 
Private Sub Command4_Click() 
    If c(1) Is Nothing Then Set c(1) = CreateObject("Project1.Class1") 
    Call c(1).ReleaseIt 
End Sub 


Private Sub Form_Unload(Cancel As Integer) 
    Set c(0) = Nothing 
    Set c(1) = Nothing 
    End 
End Sub 

前两个命令按钮锁定它们各自的互斥锁。第二两免费。注意在互斥锁被锁定之前,显示一个唯一的线程ID。这让我相信只有线程应该阻塞,而不是冻结整个应用程序。

任何援助将不胜感激。谢谢。

编辑:我忘了提及一个非常重要的部分:在项目属性部分,我已经设置为创建'每个对象的线程',这与msghox App.ThreadID调用的结果验证。

回答

3

虽然你可以让一个类创建另一个线程(使用ActiveX EXE hack),你仍然有一个执行线程,即所有的调用都被序列化。

如果您想要一个异步调用交叉线程,您需要在该函数中设置一个定时器(SetTimer() API),并在执行长时间运行的代码之前等待回调。还要注意,当该线程被锁定时,除非可以打破并呼叫DoEvents,否则不能拨打任何电话。

0

为了避免应用程序的锁定,应至少在调用CreateThread时在应用程序中的某个位置。

问题是,您拥有的所有代码都是在单个线程(主应用程序线程)上执行的。所以当你点击按钮时,主线程将会阻塞WaitForSingleObject直到互斥体被释放。由于主线程被阻塞,应用程序的UI会冻结(消息循环被阻止),因此您无法单击其他按钮来释放互斥锁。

编辑:即使每个对象都有自己的线程,似乎调用类方法是同步的。这意味着即使LockIt方法中的代码在另一个线程中执行,调用线程(在您的情况下为UI线程)也会一直等到LockIt方法结束。您可以通过将消息框放在Command1_ClickCommand2_Click的末尾来轻松检查。这些消息框只有在显示LockIt的所有消息框后才会出现,而不是在您调用LockIt方法后立即显示。 (我认为用一些保存到文件的日志消息替换MessageBox会更好)。作为一个结论,似乎你将线程的同步作为默认行为,因此可能不需要使用互斥锁。

+0

Ahh-我忘了提及一个非常重要的部分:在项目属性部分,我已经设置为创建'每个对象的线程',并且这通过msghox App.ThreadID调用的结果进行验证。关于使用相同的互斥体名称,这是必要的,因为我有各种变量需要保护。在这个演示应用程序中,没有任何内容实际上受到保护,但在应用程序中,我使用互斥体名称作为共享变量/内存的名称,以便即使我使用了互斥体,也不会冻结另一个线程的互斥体 - 因此只要它是一个不同的互斥体即可。 – 2011-12-19 20:19:44

+0

@AuthmanApatira你能解释一下应用程序何时会阻塞(我没有安装VB6)。 – 2011-12-19 21:01:04

+0

我会点击第一个锁按钮,并且会通知我线程ID,等待,然后我传入的字符串(object0)提醒我它已成功获取互斥锁。当我点击第二个锁按钮时,它会向我发送一个不同的,唯一的线程ID,等待---然后游戏结束。如果我将dowhile循环注释掉,并且只留下waitforsingleobject行,它将返回-1(失败)并立即继续处理该函数。所以这些事件实际上并没有帮助/做任何事情。我在想,因为它的一个新的线程,也许它需要处理窗口消息,因此msgwait线,但iono – 2011-12-19 22:53:43