231

AFAIK、それが知っているのは、ある時点で、そのSetResultまたはSetExceptionメソッドが呼び出されて、そのプロパティTask<T>を介して公開を完了することです。Task

言い換えれば、それはaTask<TResult>とその完成のプロデューサーとして機能します。

私はここで例を見ました:

Func<T>非同期で実行する方法が必要で、Task<T> その操作を表す方法が必要な場合。

public static Task<T> RunAsync<T>(Func<T> function) 
{ 
    if (function == null) throw new ArgumentNullException(“function”); 
    var tcs = new TaskCompletionSource<T>(); 
    ThreadPool.QueueUserWorkItem(_ => 
    { 
        try 
        {  
            T result = function(); 
            tcs.SetResult(result);  
        } 
        catch(Exception exc) { tcs.SetException(exc); } 
    }); 
    return tcs.Task; 
}

私が持っていなかった場合に使用できますTask.Factory.StartNew-しかし、私持っていますTask.Factory.StartNew

質問:

誰かが、私が持っていない架空の状況に 直接関連するシナリオを例として説明できますか?TaskCompletionSourceTask.Factory.StartNew

4

10 に答える 10

262

私は主に、イベントベースのAPI( Windows Phone 8ソケットなど)のみが利用可能な場合に使用します。

public Task<Args> SomeApiWrapper()
{
    TaskCompletionSource<Args> tcs = new TaskCompletionSource<Args>(); 

    var obj = new SomeApi();

    // will get raised, when the work is done
    obj.Done += (args) => 
    {
        // this will notify the caller 
        // of the SomeApiWrapper that 
        // the task just completed
        tcs.SetResult(args);
    }

    // start the work
    obj.Do();

    return tcs.Task;
}

asyncしたがって、C#5キーワードと一緒に使用すると特に便利です。

于 2013-03-09T22:31:23.157 に答える
84

私の経験でTaskCompletionSourceは、古い非同期パターンを最新のasync/awaitパターンにラップするのに最適です。

私が考えることができる最も有益な例は、で作業するときSocketです。古いAPMおよびEAPパターンがありますが、それらのawaitable TaskメソッドはTcpListenerありませんTcpClient

私は個人的にNetworkStreamクラスにいくつかの問題があり、生を好みSocketます。このパターンも気に入っているので、のいくつかの拡張メソッドを作成async/awaitする拡張クラスを作成しました。SocketExtenderSocket

これらのメソッドはすべて、次のTaskCompletionSource<T>ように非同期呼び出しをラップするために使用します。

    public static Task<Socket> AcceptAsync(this Socket socket)
    {
        if (socket == null)
            throw new ArgumentNullException("socket");

        var tcs = new TaskCompletionSource<Socket>();

        socket.BeginAccept(asyncResult =>
        {
            try
            {
                var s = asyncResult.AsyncState as Socket;
                var client = s.EndAccept(asyncResult);

                tcs.SetResult(client);
            }
            catch (Exception ex)
            {
                tcs.SetException(ex);
            }

        }, socket);

        return tcs.Task;
    }

メソッドにを渡してsocketBeginAcceptローカルパラメータを上げる必要がないコンパイラからわずかなパフォーマンスの向上を得ることができます。

次に、そのすべての美しさ:

 var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 listener.Bind(new IPEndPoint(IPAddress.Loopback, 2610));
 listener.Listen(10);

 var client = await listener.AcceptAsync();
于 2014-02-01T18:29:00.160 に答える
39

私にとって、使用するための典型的なシナリオはTaskCompletionSource、私のメソッドが必ずしも時間のかかる操作を実行する必要がない可能性がある場合です。それが私たちにできることは、新しいスレッドを使用したい特定のケースを選択することです。

この良い例は、キャッシュを使用する場合です。要求されたリソースをキャッシュで検索し、リソースが見つかった場合はGetResourceAsync(新しいスレッドを使用せずに、を使用して)すぐに返すメソッドを作成できます。TaskCompletionSourceリソースが見つからなかった場合にのみ、新しいスレッドを使用し、を使用してそれを取得しますTask.Run()

コード例はここで見ることができます:タスクを使用してコードを条件付きで非同期的に実行する方法

于 2013-03-10T07:16:19.557 に答える
30

このブログ投稿では、Levi Botelhoが、を使用しTaskCompletionSourceてプロセスの非同期ラッパーを作成し、プロセスを起動して終了を待つ方法について説明しています。

public static Task RunProcessAsync(string processPath)
{
    var tcs = new TaskCompletionSource<object>();
    var process = new Process
    {
        EnableRaisingEvents = true,
        StartInfo = new ProcessStartInfo(processPath)
        {
            RedirectStandardError = true,
            UseShellExecute = false
        }
    };
    process.Exited += (sender, args) =>
    {
        if (process.ExitCode != 0)
        {
            var errorMessage = process.StandardError.ReadToEnd();
            tcs.SetException(new InvalidOperationException("The process did not exit correctly. " +
                "The corresponding error message was: " + errorMessage));
        }
        else
        {
            tcs.SetResult(null);
        }
        process.Dispose();
    };
    process.Start();
    return tcs.Task;
}

とその使用法

await RunProcessAsync("myexecutable.exe");
于 2014-08-30T12:09:56.827 に答える
16

TaskCompletionSourceは、コードを実行しないタスクオブジェクトを作成するために使用されます。実際のシナリオでは、TaskCompletionSourceはI/Oバウンド操作に最適です。このようにして、操作中にスレッドをブロックすることなく、タスクのすべての利点(たとえば、戻り値、継続など)を取得できます。「関数」がI/Oバウンド操作である場合、新しいタスクを使用してスレッドをブロックすることはお勧めしません。代わりに、TaskCompletionSourceを使用して、I/Oバウンド操作が終了または失敗したことを示すだけのスレーブタスクを作成できます。

于 2014-04-25T09:53:13.357 に答える
16

誰も言及していないように見えますが、単体テストも実際の生活と見なすことができると思います。

TaskCompletionSource非同期メソッドで依存関係をモックするときに便利だと思います。

テスト中の実際のプログラム:

public interface IEntityFacade
{
  Task<Entity> GetByIdAsync(string id);
}

単体テストの場合:

// set up mock dependency (here with NSubstitute)

TaskCompletionSource<Entity> queryTaskDriver = new TaskCompletionSource<Entity>();

IEntityFacade entityFacade = Substitute.For<IEntityFacade>();

entityFacade.GetByIdAsync(Arg.Any<string>()).Returns(queryTaskDriver.Task);

// later on, in the "Act" phase

private void When_Task_Completes_Successfully()
{
  queryTaskDriver.SetResult(someExpectedEntity);
  // ...
}

private void When_Task_Gives_Error()
{
  queryTaskDriver.SetException(someExpectedException);
  // ...
}

結局のところ、このTaskCompletionSourceの使用法は、「コードを実行しないタスクオブジェクト」の別のケースのようです。

于 2015-07-10T11:26:23.920 に答える
5

この投稿には、「.NETを使用した並列プログラミング」ブログからの適切な説明を含む実際の例があります。あなたは本当にそれを読むべきです、しかしここにとにかく要約があります。

ブログ投稿には、次の2つの実装が示されています。

「「遅延」タスクを作成するためのファクトリメソッド。ユーザーが指定したタイムアウトが発生するまで実際にはスケジュールされません。」

示されている最初の実装は、それに基づいてTask<>おり、2つの大きな欠陥があります。2番目の実装投稿では、を使用してこれらを軽減しTaskCompletionSource<>ます。

2番目の実装は次のとおりです。

public static Task StartNewDelayed(int millisecondsDelay, Action action)
{
    // Validate arguments
    if (millisecondsDelay < 0)
        throw new ArgumentOutOfRangeException("millisecondsDelay");
    if (action == null) throw new ArgumentNullException("action");

    // Create a trigger used to start the task
    var tcs = new TaskCompletionSource<object>();

    // Start a timer that will trigger it
    var timer = new Timer(
        _ => tcs.SetResult(null), null, millisecondsDelay, Timeout.Infinite);

    // Create and return a task that will be scheduled when the trigger fires.
    return tcs.Task.ContinueWith(_ =>
    {
        timer.Dispose();
        action();
    });
}
于 2014-07-14T13:17:38.077 に答える
4

これは物事を単純化しすぎているかもしれませんが、TaskCompletionソースを使用するとイベントを待つことができます。tcs.SetResultはイベントが発生したときにのみ設定されるため、呼び出し元はタスクを待機できます。

詳細については、このビデオをご覧ください。

http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Lucian03-TipsForAsyncThreadsAndDatabinding

于 2015-04-02T18:58:18.990 に答える
3

私が使用した実際のシナリオはTaskCompletionSource、ダウンロードキューを実装する場合です。私の場合、ユーザーが100回のダウンロードを開始した場合、一度にすべてを起動したくないので、階層化されたタスクを返す代わりに、に添付されたタスクを返しますTaskCompletionSource。ダウンロードが完了すると、キューを処理しているスレッドがタスクを完了します。

ここでの重要な概念は、クライアントがタスクを実際に開始したときから開始するように要求したときに、デカップリングしているということです。この場合、クライアントがリソース管理を処理する必要がないためです。

C#5コンパイラ(VS 2012+)を使用している限り、.net4でasync/awaitを使用できることに注意してください。詳細についてはこちらを参照してください。

于 2014-01-08T19:20:32.093 に答える
1

TaskCompletionSourceキャンセルされるまでタスクを実行していました。この場合、アプリケーションが実行されている限り、通常は実行したいのはServiceBusサブスクライバーです。

public async Task RunUntilCancellation(
    CancellationToken cancellationToken,
    Func<Task> onCancel)
{
    var doneReceiving = new TaskCompletionSource<bool>();

    cancellationToken.Register(
        async () =>
        {
            await onCancel();
            doneReceiving.SetResult(true); // Signal to quit message listener
        });

    await doneReceiving.Task.ConfigureAwait(false); // Listen until quit signal is received.
}
于 2019-01-03T09:28:33.977 に答える