【问题标题】:Mutex freezing the whole app when using multithreading使用多线程时互斥锁冻结整个应用程序
【发布时间】:2011-12-19 19:18:23
【问题描述】:

我有一个正在运行的 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 调用的结果进行了验证。

【问题讨论】:

    标签: multithreading vb6 mutex


    【解决方案1】:

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

    如果您想要一个异步调用跨线程,您需要在该函数中设置一个计时器(SetTimer() API)并等待回调,然后再执行长时间运行的代码。另请注意,当该线程被锁定时,您不能对其进行任何调用,除非它们可以中断并调用 DoEvents

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 啊-我忘了提到一个非常重要的部分:在项目属性部分,我将其设置为创建“每个对象的线程”,这已通过 msghox App.ThreadID 调用的结果进行了验证。关于使用相同的互斥体名称,这是必要的,因为我需要保护各种变量。在这个演示应用程序中,实际上没有任何东西受到保护,但是在应用程序中,我使用互斥锁名称作为共享变量/内存的名称,这样即使我使用了互斥锁,它也不会冻结另一个线程的互斥锁——所以只要它是不同的互斥体。
      • @AuthmanApatira 你能解释一下应用程序何时会阻塞(我没有安装 VB6)。
      • 我将单击第一个锁定按钮,它会向我发送线程 ID,等待,然后我传入的字符串 (object0) 提醒我它已成功获取互斥锁。当我点击第二个锁定按钮时,它会向我发送一个不同的、唯一的线程 ID,等待 --- 然后游戏结束。如果我注释掉 dowhile 循环并离开 waitforsingleobject 行,它将返回 -1(失败)并立即继续处理该函数。所以doevents实际上并没有帮助/做任何事情。我在想,因为它是一个新线程,也许它需要处理窗口 msgs,因此 msgwait 行,但是 iono
      • @bosonix:请参阅this article,了解所使用方法的说明。
      猜你喜欢
      • 2019-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-19
      相关资源
      最近更新 更多