我有一个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调用的结果进行验证。答案 0 :(得分:3)
虽然你可以让一个类创建另一个线程(使用ActiveX EXE hack),你仍然有一个执行线程,即所有的调用都是序列化的。
如果你想要一个异步调用交叉线程,你需要在该函数中设置一个定时器(SetTimer()
API)并在执行长时间运行的代码之前等待回调。另请注意,虽然该线程已被锁定,但您无法对其进行任何调用,除非它们可以中断并调用DoEvents
。
答案 1 :(得分:0)
为了避免锁定应用程序,您应该在应用程序中的某个位置至少调用CreateThread。
问题是你拥有的所有代码都是在一个主线程,即主应用程序线程上执行的。因此,当您单击该按钮时,主线程将在WaitForSingleObject中阻塞,直到释放互斥锁。由于主线程被阻止,应用程序的UI冻结(消息循环被阻止),因此您无法单击其他按钮以释放互斥锁。
编辑:即使每个对象都有自己的线程,似乎对类方法的调用也是同步的。这意味着调用线程(在您的情况下是UI线程)将等待LockIt方法结束,即使LockIt方法中的代码在另一个线程中执行。您可以通过在Command1_Click
和Command2_Click
末尾添加MessageBox来轻松地进行检查。只有在显示LockIt的所有消息框后才会显示这些消息框,而不是在调用LockIt方法后立即显示。 (我认为最好用保存在文件中的某种日志消息替换MessageBox)。作为结论,似乎您将线程同步作为默认行为,因此您可能不需要使用互斥锁。