2012-04-03 9 views
1

これは間違いなく疑問に思うかもしれませんが、これはすでに別の場所で回答されている場合は、検索が表示されないので、何か決定的。一言で言えばThread.JoinもUIスレッドで子スレッドをブロックしています


、私の問題は、私はchildThreadを停止するようにフラグが付けられている子スレッドのUIスレッドでchildThread.Join()を行うときにブロックするように思えるだけでなく、すべてのでメインスレッドということですちょうどハングアップします。
Joinを使用したためにUIがブロックされることは、とにかく終了するように指示された後、childThreadが1秒以内に終了する必要があるため、それ自体は問題ではありません。
これは、何らかの情報を返すが他のプロセスと同時に実行できない別のメソッドを実行する前に、繰り返しプロセスを実行しているスレッドが終了するのを待っている間に発生します。

私のWinformsアプリケーションは、ハードウェア用のC APIをピンボーキングすることによって、USBハードウェアと統合しています。

ハードウェアAPIには、無期限に繰り返し実行されるプロセスを開始するメソッドがあり、新しい情報を迅速にコールバックしてUIに渡す必要があります。
この操作は、ハードウェアAPIの別の呼び出しによって取り消すことができます。この呼び出しは、ハードウェアが認識できるようにフラグを設定して終了することができます。
私はこのC APIを独自のC#コードでラップしています。ラッパー内では、アクティビティがUIをブロックしないように、別のスレッドで開始プロセス呼び出しを開始する必要がありました。

ここには、私がやっていることの大まかな編集ハイライトがあります。私はchildThread.Join()アプリケーション全体が(私はUIのために期待する、それは大丈夫です)停止して磨くとchildThreadもコールバックが取得することはありませんので、止めるように思わ呼び出し、このコードを使用して

public class DeviceWrapper 
{ 
    Thread childThread = null; 

    void DeviceWrapper 
    { 
     //Set the callback to be used by the StartGettingInformation() process 
     PInvokeMethods.SetGetInformationCallback(InformationAcquiredCallback); 
    } 

    public void StartProcess() 
    { 
     childThread = new Thread(new ThreadStart(GetInformationProcess)) 
     childThread.Start(); 
    } 

    void GetInformationProcess() 
    { 
     PInvokeMethods.StartGettingInformation(); 
    } 

    //This callback occurs inside the childThread 
    void InformationAcquiredCallback(Status status, IntPtr information) 
    { 
     //This callback is triggered when anything happens in the 
     //StartGettingInformation() method, such as when the information 
     //is ready to be retrieved, or when the process has been cancelled. 
     if(status == Status.InformationAcquired) 
     { 
      FireUpdateUIEvent(); 
     } 
     //If the cancel flag has been set to true this will be hit. 
     else if(status == Status.Cancelled) 
     { 
      //Reset the cancel flag so the next operation works ok 
      PInvokeMethods.SetCancelFlag(false); 

      childThread.Abort(); 
     } 
    } 

    //This method runs once, and can't run at the same time as GetInformationProcess 
    public string GetSpecificInformation() 
    { 
     //This triggers InformationAcquiredCallback with a status of Cancelled 
     StopProcess(); 

     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 

     return PInvokeMethods.GetSpecificInformation(); 
    } 

    public void StopProcess() 
    { 
     PInvokeMethods.SetCancelFlag(true); 
    } 
} 

もう一度ヒットします。

私が代わりに次のコードを使用する場合は、:

public string GetSpecificInformation() 
{ 
    //This triggers InformationAcquiredCallback with a status of Cancelled 
    StopProcess(); 
    string s = ""; 

    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate 
    { 
     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 
     s = PInvokeMethods.GetSpecificInformation();    
    })); 

    return s; 
} 

そして、すべてが期待通りにヒット取得し、childThreadが完了しないと明らかに私の文字列がWaitCallback火災の前に空の返却とに割り当てます以外のすべてが、うまくありますそれ。

私はQueueUserWorkItemとWaitCallbackを使用してイベントを発生させて文字列を返すように、それを吸い取ってクラスを変更するだけですか?
childThreadもブロックする原因になっています。
または、私が使用しているはずの別の戦術またはクラスがあります。.NET 3.5のことを覚えておいてください。

+3

私は過去30年間、TThread.WaitFor()とデッドロックを生成するハードロック同期メカニズムを使用してDelphi開発者を止めようとしました。ちょうど私がどこかに行っていると思うと、JavaとC#の開発者はjoin()を発見します。終わりのない悪夢。 –

+0

私が絶望的で奥行きのない限り、私はこれに触れていないと信じています;)だから代わりに何をお勧めしますか? – Nanhydrin

+0

あなたはエリックと他の人の話を聞いてください。 Invoke、BeginInvokeなどは間違っていません。イベントハンドラで待機しないでください - 他のスレッドが何か言いたいことがあるときに、デリゲートがメインスレッドで起動されることによって返されるシグナルに作用してください。 –

答えて

5

まあ、FireUpdateUIEvent(); ポスト MsgQueue(Control.Invoke())へ送るかもしれない方法のように聞こえます。メインスレッドがJoin()で待機しているときは、古典的なデッドロックが発生しています。

さらに、childThread.Abort()は安全とはみなされません。だから、

、私はちょうどそれを吸うと私はQueueUserWorkItemとWaitCallbackを使用するようにクラスを変更し、私の文字列戻り値に対処するためのイベントを発生しなければなりませんか?

私は確かにそれを再設計します。それはおそらく少し簡素化することができます。

+0

FireUpdateUIEventはUI自体を直接更新するわけではありませんが、UIによって正しくハンドラが提供され、そのコールバックは返すのを待つことをブロックします。しかし、実際に値や何か気にするものが返ってくるわけではないので、おそらくそれを別のスレッドに振り向けることができます。 – Nanhydrin

+0

@ナンヒドリン:ヘンクは正しい。全体を再設計する必要があります。 UIスレッドからのスレッドアボートまたはジョインのいずれも実行してはいけません。別のスレッドで面白いことが起こったことをUIスレッドに伝えたい場合は、オペレーティングシステムと同じことをします。メッセージをキューに入れます。 –

+0

FireUpdateUIEventは、Control.InvokeではなくControl.BeginInvokeを使用する必要があります。それはデッドロックを解決するかもしれない。しかし、Join()とAbort()はまだ良い状態ではありません。 –

関連する問題