2011-12-19 2 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가 고정되어 (메시지 루프가 차단됨) 다른 단추를 클릭하여 뮤텍스를 해제 할 수 없습니다.

EDIT : 모든 개체가 자체 스레드를 가지고 있더라도 클래스 메서드 호출이 동기화 된 것처럼 보입니다. 즉, 호출 스레드 (경우에 따라 UI 스레드)는 LockIt 메서드의 코드가 다른 스레드에서 실행되는 경우에도 LockIt 메서드가 끝날 때까지 대기합니다. Command1_ClickCommand2_Click 끝에 MessageBox를 넣으면 쉽게 확인할 수 있습니다. 이러한 메시지 상자는 LockIt 메서드를 호출 한 직후가 아니라 LockIt의 모든 메시지 상자가 표시된 후에 만 ​​나타납니다. (필자는 MessageBox를 파일에 저장된 일종의 로그 메시지로 대체하는 편이 낫다고 생각합니다.) 결론적으로 스레드의 동기화를 기본 동작으로하는 것처럼 보이므로 뮤텍스를 사용할 필요가 없을 것입니다.

+0

Ahh- 저는 매우 중요한 부분을 잊어 버렸습니다. 프로젝트 속성 섹션에서 '개체 당 스레드'를 만들도록 설정했으며이 작업은 msghox App.ThreadID 호출의 결과와 함께 확인됩니다. 동일한 뮤텍스 이름을 사용하는 것과 관련하여 필자는 보호해야 할 다양한 변수가 있기 때문에 필연적입니다.이 데모 응용 프로그램에서는 실제로 아무 것도 보호되지 않지만 응용 프로그램에서는 공유 변수/메모리의 이름으로 뮤텍스 이름을 사용하므로 뮤텍스가 사용 되더라도 다른 스레드의 뮤텍스가 고정되지 않습니다. 그것은 다른 뮤텍스이기 때문에 오래. –

+0

@AuthmanApatira는 응용 프로그램이 차단 될 때 설명 할 수 있습니까 (VB6가 설치되어 있지 않습니다). –

+0

첫 번째 잠금 버튼을 클릭하면 스레드 ID, 기다리고 나서 문자열 (object0)에 전달되어 뮤텍스를 성공적으로 획득했음을 알립니다. 두 번째 잠금 버튼을 클릭하면 다른 고유 한 스레드 ID를 기다리고 있습니다. --- 기다린 다음 게임을 끝내십시오. 내가 dowhile 루프를 주석 처리하고 waitforsingleobject 행을 남겨두면, -1이 반환되고 (실패) 즉시 함수 처리가 계속됩니다. doevents는 실제로 아무 것도 돕거나 수행하지 않습니다. 나는 새 스레드, 아마도 윈도우 msgs를 처리해야하기 때문에 생각하고 있었다. 따라서 msgwait 라인 이오노 –

관련 문제