Mutex замораживает все приложение при использовании многопоточности - PullRequest
2 голосов
/ 19 декабря 2011

У меня работает многопоточное приложение 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

Первые две кнопки команд блокируют соответствующие мьютексы.Вторые два освобождают это.Обратите внимание, как перед блокировкой мьютекса отображается уникальный идентификатор потока.Это заставило меня поверить, что только поток должен блокировать, а не замораживать все приложение.

Любая помощь будет принята с благодарностью.Спасибо.

РЕДАКТИРОВАТЬ: я забыл упомянуть очень важную часть: в разделе свойств проекта, он настроен на создание «поток на объект», и это подтверждается результатами вызовов msghox App.ThreadID.

Ответы [ 2 ]

3 голосов
/ 20 декабря 2011

Хотя вы можете заставить класс создать другой поток (используя ActiveX EXE-хак ), у которого все еще есть один поток выполнения, то есть все вызовы сериализуются.

Если вы хотите асинхронный кросс-поток, вам нужно установить таймер (SetTimer() API) в этой функции и дождаться обратного вызова, прежде чем выполнять долго выполняющийся код. Также обратите внимание, что пока этот поток заблокирован, вы не можете делать ЛЮБЫЕ вызовы в него, если они не могут прерваться и вызвать DoEvents.

0 голосов
/ 19 декабря 2011

Чтобы избежать блокировки приложения, вы должны иметь где-то в своем приложении хотя бы один вызов при вызове CreateThread.

Проблема в том, что весь ваш код выполняется в одном потоке, основнойветка приложения.Поэтому, когда вы нажимаете кнопку, основной поток блокируется в WaitForSingleObject, пока мьютекс не будет освобожден.Поскольку основной поток заблокирован, пользовательский интерфейс приложения зависает (цикл сообщений блокируется), поэтому вы не можете нажать другую кнопку, чтобы освободить мьютекс.

РЕДАКТИРОВАТЬ: даже если каждый объектимеет свой собственный поток, кажется, вызовы методов класса синхронизированы.Это означает, что вызывающий поток (в вашем случае поток пользовательского интерфейса) будет ожидать завершения метода LockIt, даже если код в методе LockIt выполняется в другом потоке.Вы можете легко проверить это, поместив MessageBox в конце Command1_Click и Command2_Click.Эти окна сообщений появятся только после того, как будут отображены все окна сообщений из LockIt, а не сразу после вызова метода LockIt.(Я думаю, что лучше заменить MessageBox каким-либо лог-сообщением, сохраненным в файл).В заключение кажется, что вы получаете синхронизацию потоков как поведение по умолчанию, поэтому, вероятно, вам не нужно использовать мьютексы.

...