2011-12-19 7 views
2

私はvb6マルチスレッドアプリケーションを動作させています。データを保護するためにmutexを使用したいと思います。期待される振る舞いは、スレッドが既存のミューテックスのロックを取得しようとしたときに、「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 

最初の2つのコマンドボタンはそれぞれのミューテックスをロックします。 2番目の2つは無料です。ミューテックスがロックされる前に、一意のスレッドIDが表示されることに注意してください。これにより、そのスレッドだけがブロックされ、アプリケーション全体がフリーズしないと信じられました。

ご協力いただければ幸いです。ありがとうございました。

編集:私は非常に重要な部分を忘れていました:プロジェクトのプロパティセクションでは、私はそれがオブジェクトごとのスレッドを作成するように設定しており、これはmsghox App.ThreadID呼び出しの結果で検証されます。

答えて

3

クラスを作成して別のスレッド(ActiveX EXE hackを使用)を作成することはできますが、実行スレッドは1つだけです。つまり、すべての呼び出しがシリアル化されます。

非同期コールクロススレッドが必要な場合は、その関数でタイマー(SetTimer() API)を設定し、長時間実行するコードを実行する前にコールバックを待つ必要があります。また、スレッドがロックされている間は、ブレークしてDoEventsを呼び出すことができない限り、そのスレッドを呼び出すことはできません。

0

アプリケーションがロックされないようにするには、少なくともCreateThreadを呼び出してアプリケーションのどこかにあるべきです。

問題は、すべてのコードが1つのスレッド、メインのアプリケーションスレッドで実行されることです。したがって、ボタンをクリックすると、メインスレッドはミューテックスが解放されるまでWaitForSingleObjectでブロックされます。メインスレッドはブロックされているため、アプリケーションのUIがフリーズし(メッセージループがブロックされるため)、他のボタンをクリックしてミューテックスを解放することはできません。

EDIT:すべてのオブジェクトに独自のスレッドがある場合でも、クラスメソッドの呼び出しは同期されているようです。これは、LockItメソッドのコードが別のスレッドで実行されていても、呼び出しスレッド(場合によってはUIスレッド)がLockItメソッドが終了するまで待機することを意味します。 Command1_ClickCommand2_Clickの末尾にMessageBoxを置くと簡単に確認できます。これらのメッセージボックスは、LockItメソッドを呼び出した直後ではなく、LockItのすべてのメッセージボックスが表示された後に表示されます。 (私はそれがファイルに保存されたいくつかの種類のログメッセージでMessageBoxを置き換える方が良いと思う)。結論として、スレッドの同期をデフォルトの動作として得ていると思われるので、多分mutexを使う必要はありません。

+0

Ahh-私は非常に重要な部分について言及するのを忘れていました:プロジェクトプロパティのセクションで、私はそれを 'オブジェクトごとのスレッド'を作成するように設定しました。これはmsghox App.ThreadID呼び出しの結果で検証されます。同じミューテックス名を使用することに関しては、私が保護する必要がある様々な変数があるため、これは必要です。このデモアプリケーションでは、実際に何も保護されていませんが、アプリケーションでは共有変数/メモリの名前としてmutex名を使用していますので、mutexを使用していても別のスレッドのmutexをフリーズしませんそれは異なるミューテックスである限り長い。 –

+0

@AuthmanApatiraは、アプリケーションがブロックする(VB6がインストールされていない)ときに説明してください。 –

+0

私は最初のロックボタンをクリックして、スレッドID、待機中、次に文字列iが(object0)で渡され、mutexを正常に取得したことを警告します。 2番目のロックボタンをクリックすると、別のユニークなスレッドIDを待っています---待ってからゲームオーバーします。私がdowhileループをコメントアウトしてwaitforsingleobject行を残すと、-1が返され(失敗)、すぐに関数の処理が続行されます。 doeventsは実際に助けて/何もしていません。私は考えていたのは、新しいスレッド、多分ウィンドウのmsgsを処理する必要があるため、msgwait行だが、イオノ –

関連する問題