2011-12-19 73 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