5

私には2つの方法がありMethodAますMethodBMethodBUIスレッドで実行する必要があります。私はそれらの間を実行することを許可せずに次々に実行する必要がありMethodCます。

MethodCユーザーが素敵な小さなボタンをクリックすると呼び出されます。

これを確実にするために私がしたことは、このLockようにコードの周りに置かれます:

 lock (MyLock)
 {
   MethodA(param1, param2);

   MyDelegate del = new MyDelegate(MethodB);
   if (this.IsHandleCreated) this.Invoke(del);
 }

そしてのためにMethodC

public void MethodC()
 lock (MyLock)
 {
   Do bewildering stuff.....
 }

問題は私が立ち往生していることです。コードがデッドロックに陥っているようです。

スレッドを見ると、ボタンのクリックによって呼び出されたコードがスタックしlock (MyLock)MethodCいて、他のスレッドがでスタックしているように見えthis.Invoke(del)ます。

内からメソッドを呼び出すのは危険だと読んだのLockですが、そこにコードを書いたのはThread.Sleep私なので、これは私を困らせているのはコードではないと思っただけでも起こるようです。

Invokedメソッドが機能しなくなるのはなぜですか?ロックが解除されるのを待ってmethodCから、元のロックに戻る可能性がありますか?

4

4 に答える 4

10

したがって、次の状況を想像してください。

  1. バックグラウンド スレッドがコードの実行を開始します。ロックを取得し、実行を開始しますMethodA

  2. MethodCMethodAが作業中に 呼び出されます。MethodAロックが解放されるまで待機し、解放されるまで UI スレッドをブロックします。

  3. バックグラウンド スレッドが終了し、UI スレッドで MethodA呼び出されます。メッセージ ポンプのキュー内の以前の項目がすべて終了するまで実行できません。MethodBMethodB

  4. MethodCメッセージポンプのキューの一番上にあり、MethodB終了するまで待機しており、終了MethodBするまで待機しているキューにありますMethodC。どちらもお互いに待機しているため、デッドロックになっています。

では、この問題をどのように解決しますか? 本当に必要なの、スレッドを実際にブロックせずにロックを「待機」する方法です。幸いなことに (.NET 4.5 では)、これは Task Parallel Library のおかげで簡単に実行できます。(実際には待機したくないので、引用符で待機しています。現在のスレッドを実際に待機/ブロックせずMethodCに、ロックが解放されたらすぐに実行したいだけです。)

objectfor useを使用する代わりにMyLock:

private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);

これで、次のMethodCことができます:

public async Task MethodC() //you can change the signature to return `void` if this is an event handler
{
    try
    {
        await semaphore.WaitAsync();
        //Do stuff
    }
    finally
    {
        semaphore.Release();
    }
}

ここで重要なawaitのは、セマフォが実際に解放されるタイミングを表すタスクであるため、現在のスレッドをブロックしていないことです。これにより、他のバックグラウンド タスクがMethodBUI スレッドにマーシャリングし、メソッドを終了し、セマフォを解放してから、このメソッドを実行させます。

他のコードでは、セマフォで非同期待機を使用する必要はありません (ただし、必要に応じて使用できます)。バックグラウンド スレッドのブロックはそれほど問題ではないため、唯一の重要な変更点は、代わりにセマフォを使用することですlock

public void Bar()
{
    try
    {
        semaphore.Wait();
        MethodA(param1, param2);

        MyDelegate del = new MyDelegate(MethodB);
        if (this.IsHandleCreated) this.Invoke(del);
    }
    finally
    {
        semaphore.Release();
    }
}
于 2013-02-13T18:20:19.627 に答える
-1

あなたはむしろこれを試すことができます:

Lock (MyLock)
 {
   MethodA(param1, param2);

   MyDelegate del = new MyDelegate(MethodB);
   MyDelegate del2 = new MyDelegate(MethodC);
   MyDelegate del3 = del+del2
   if (this.IsHandleCreated) this.Invoke(del3);
 }
于 2013-02-13T18:28:09.237 に答える
-1

あなたはロックを使っている人々の地獄を混乱させました。このタスクは、マルチスレッド自体とは何の関係もありません。

シンプルな使いやすさのソリューションが必要です。MethodCを実行する準備ができるまで、素敵な小さなボタンを無効にしてください。

于 2013-02-14T03:14:55.640 に答える
-1

MethodA次のようなものも含まれていると仮定します。

lock(MyLock) {

}

あなたは完全に正しいです、あなたはデッドロックを持っています。 メソッドに入る前にすでにロックされているためMethodA、ロックを取得できません。MyLock

于 2013-02-13T18:18:23.590 に答える