3

コード全体で async/await パターンを使用しています。ただし、イベント ベースの非同期パターンを使用する API が 1 つあります。MSDN といくつかの StackOverflow の回答を読みましたが、これを行う方法は TaskCompletionSource を使用することです。

私のコード:

public static Task<string> Process(Stream data)
{
    var client = new ServiceClient();
    var tcs = new TaskCompletionSource<string>();

    client.OnResult += (sender, e) =>
    {
        tcs.SetResult(e.Result);
    };

    client.OnError += (sender, e) =>
    {
        tcs.SetException(new Exception(e.ErrorMessage));
    };

    client.Send(data);

    return tcs.Task;
}

そして次のように呼ばれます:

string result = await Process(data);

または、テスト用:

string result = Process(data).Result;

メソッドは常に非常に迅速に戻りますが、どちらのイベントもトリガーされません。

tcs.Task.Await(); を追加すると return ステートメントの直前では機能しますが、これは私が望む非同期動作を提供しません。

インターネットで見たさまざまなサンプルと比較しましたが、違いはわかりません。

4

2 に答える 2

8

問題は、Processメソッドが終了した後、ServiceClientローカル変数がガベージ コレクションの対象となり、イベントが発生する前に収集される可能性があるため、競合状態が発生するという事実にあります。

これを避けるために、型の拡張メソッドProcessAsyncとして次のように定義します。

public static class ServiceClientExtensions
{
    public static Task<string> ProcessAsync(this ServiceClient client, Stream data)
    {
        var tcs = new TaskCompletionSource<string>();

        EventHandler resultHandler = null;
        resultHandler = (sender, e) => 
        {
            client.OnResult -= resultHandler;
            tcs.SetResult(e.Result);
        }

        EventHandler errorHandler = null;
        errorHandler = (sender, e) =>
        {
            client.OnError -= errorHandler;
            tcs.SetException(new Exception(e.ErrorMessage));
        };

        client.OnResult += resultHandler;
        client.OnError += errorHandler;

        client.Send(data);
        return tcs.Task;
    }
}

そして、次のように消費します。

public async Task ProcessAsync()
{
    var client = new ServiceClient();
    string result = await client.ProcessAsync(stream);
}

編集: @usr は、通常、IO 操作は、呼び出し元への参照を維持する必要があることを指摘していますが、ここで見ているようには当てはまりません。私は、この動作が少し独特であり、オブジェクトの何らかの設計/実装の問題を示している可能性があるという彼の意見に同意しServiceClientます。可能であれば、実装を調べて、参照が根付いていない原因となるものがないかどうかを確認することをお勧めします。

于 2015-12-05T14:35:08.573 に答える
-2

これは答えるべきだと思います。

public static Task<string> Process(Stream data)
{
    var handle = new AutoResetEvent(false);
    var client = new ServiceClient();
    var tcs = new TaskCompletionSource<string>();

    client.OnResult += (sender, e) =>
    {
        tcs.SetResult(e.Result);
        handle.Set();
    };

    client.OnError += (sender, e) =>
    {
        tcs.SetException(new Exception(e.ErrorMessage));
        handle.Set();
    };

    client.Send(data);

    handle.WaitOne(10000); // wait 10 secondds for results
    return tcs.Task;
}
于 2015-12-05T14:33:04.197 に答える