1

私は WCF にいくつかの外部 Web サービスを使用する Web サービスを持っているので、このサービスを非同期にしてスレッドを解放し、すべての外部サービスの完了を待ってから、結果をクライアントに返します。 .

フレームワーク 4.0 で

public class MyService : IMyService
{
    public IAsyncResult BeginDoWork(int count, AsyncCallback callback, object serviceState)
    {    
        var proxyOne = new Gateway.BackendOperation.BackendOperationOneSoapClient();
        var proxyTwo = new Gateway.BackendOperationTwo.OperationTwoSoapClient();

        var taskOne = Task<int>.Factory.FromAsync(proxyOne.BeginGetNumber, proxyOne.EndGetNumber, 10, serviceState);
        var taskTwo = Task<int>.Factory.FromAsync(proxyTwo.BeginGetNumber, proxyTwo.EndGetNumber, 10, serviceState);
        
        var tasks = new Queue<Task<int>>();
        tasks.Enqueue(taskOne);
        tasks.Enqueue(taskTwo);

        return Task.Factory.ContinueWhenAll(tasks.ToArray(), innerTasks =>
        {
            var tcs = new TaskCompletionSource<int>(serviceState);
            int sum = 0;

            foreach (var innerTask in innerTasks)
            {
                if (innerTask.IsFaulted)
                {
                    tcs.SetException(innerTask.Exception);
                    callback(tcs.Task);
                    return;
                }

                if (innerTask.IsCompleted)
                {
                    sum = innerTask.Result;
                }
            }

            tcs.SetResult(sum);

            callback(tcs.Task);
        });
    }

    public int EndDoWork(IAsyncResult result)
    {
        try
        {
            return ((Task<int>)result).Result;
        }
        catch (AggregateException ex)
        {
            throw ex.InnerException;
        }

    }
}

ここでの私の質問は次のとおりです。

  1. このコードは 3 つのスレッドを使用しています。1 つは BeginDoWork でインスタンス化され、もう 1 つはコードが匿名メソッド ContinueWhenAll 内に入ったときにインスタンス化され、最後のスレッドはコールバックが実行されたときにインスタンス化されます (この場合は EndDoWork)。それは正しいですか、それとも電話で何か間違ったことをしていますか? 同期コンテキストを使用する必要がありますか? 注: メイン スレッドでは、同期コンテキストは null です。

  2. コールバック関数など、スレッド間で情報を「共有」するとどうなりますか? それはパフォーマンスの問題を引き起こしますか、それとも匿名メソッドはデータを共有できるクロージャーのようなものですか?

Framework 4.5 および Async と Await を使用

Framework 4.5 では、コードが以前より単純すぎるように見えます。

    public async Task<int> DoWorkAsync(int count)
    {
        var proxyOne = new Backend.ServiceOne.ServiceOneClient();
        var proxyTwo = new Backend.ServiceTwo.ServiceTwoClient();

        var doWorkOne = proxyOne.DoWorkAsync(count);
        var doWorkTwo = proxyTwo.DoWorkAsync(count);

        var result = await Task.WhenAll(doWorkOne, doWorkTwo);

        return doWorkOne.Result + doWorkTwo.Result;
    }

しかし、この場合、アプリケーションをデバッグすると、コードが同じスレッドで実行されることが常にわかります。ここでの私の質問は次のとおりです。

3.. 「待機可能な」コードを待っているとき、そのスレッドは解放され、スレッド プールに戻ってさらに要求を実行しますか?

3.1. もしそうなら、await タスクから結果を取得すると、実行は前の呼び出しと同じスレッドで完了すると思います。それは可能ですか?そのスレッドが別のリクエストを処理している場合はどうなりますか?

3.2 そうでない場合、スレッドを解放して Asycn および Await パターンでスレッド プールに戻すにはどうすればよいですか?

ありがとうございました!

4

1 に答える 1

2

1. このコードは 3 つのスレッドを使用しています。1 つは BeginDoWork でインスタンス化され、もう 1 つはコードが匿名メソッド ContinueWhenAll 内に入ったときにインスタンス化され、最後のスレッドはコールバックが実行されたときにインスタンス化されます (この場合は EndDoWork)。それは正しいですか、それとも電話で何か間違ったことをしていますか? 同期コンテキストを使用する必要がありますか?

「スレッド」ではなく「タスク」の観点から考えた方がよいでしょう。ここには 3 つのタスクがあり、それぞれが一度に 1 つずつスレッド プールで実行されます。

2. コールバック関数など、スレッド間で情報を「共有」するとどうなりますか? それはパフォーマンスの問題を引き起こしますか、それとも匿名メソッドはデータを共有できるクロージャーのようなものですか?

これらのタスクはそれぞれ同時に実行できないため、同期について心配する必要はありません。BeginDoWork戻る直前に継続を登録するので、継続が実行できるようになった時点で実質的に完了しています。EndDoWorkおそらく、継続が完了するまで呼び出されません。たとえそうであっても、継続が完了するまでブロックされます。

(技術的には、継続は完了する前に実行を開始できますが、その時点で戻るだけなので問題ありません)。BeginDoWorkBeginDoWork

3. 「待機可能な」コードを待っているとき、そのスレッドは解放され、スレッド プールに戻ってさらに要求を実行しますか?

はい。

3.1. もしそうなら、await タスクから結果を取得すると、実行は前の呼び出しと同じスレッドで完了すると思います。それは可能ですか?そのスレッドが別のリクエストを処理している場合はどうなりますか?

いいえ。ホスト (この場合は ASP.NET) は、asyncたまたま利用可能な任意のスレッドでメソッドを続行できます。

一度に 1 つのスレッドしか実行されないため、これは完全に安全です。

PSオススメです

var result = await Task.WhenAll(doWorkOne, doWorkTwo);
return result[0] + result[1];

それ以外の

var result = await Task.WhenAll(doWorkOne, doWorkTwo);
return doWorkOne.Result + doWorkTwo.Result;

プログラミングTask.Resultでは避けるべきだからです。async

于 2012-07-19T19:52:02.367 に答える