2

そのようなメソッドを含むライブラリを作成したとします:

Task MyLibraryMethodAsync()
{
    var taskCompletionSource = new TaskCompletionSource<object>();

    Action myWorkItem =
        () =>
        {
            // Simulate some work.
            // Actual work items depend on input params.
            Thread.Sleep(TimeSpan.FromSeconds(1));

            taskCompletionSource.SetResult(null);
        };

    // The next two lines is simplification for demonstration.
    // I do not have access to the workerThread - it is created
    // and managed for me by another lib.
    // All I can do - is to post some short work items to it.
    var workerThread = new Thread(new ThreadStart(myWorkItem));
    workerThread.Start();

    return taskCompletionSource.Task;
}

私のlibのすべてのユーザーは、MyLibraryMethodAsyncこのように呼び出すことができます

await MyLibraryMethodAsync().ConfigureAwait(false);
VeryLongRunningMethod();
void VeryLongRunningMethod()
{
    Thread.Sleep(TimeSpan.FromHours(1));
}

ここで問題が発生します。呼び出しVeryLongRunningMethod内で実行されるため、長時間taskCompletionSource.SetResult(null)ブロックされます。これは、コード (作業項目) の小さな部分を実行することを目的としているため、望ましくない動作です。workerThreadworkerThread

コンテキスト/スケジューラを、返されたタスク内のスレッド プールに置き換えて、スレッド プールではなくスレッド プールawait x.ConfigureAwait(false)で続行するにはどうすればよいですか?workerThread

私が見つけた現在の解決策は

Task MyLibraryMethodAsync()
{
    // ...

    return taskCompletionSource.Task
        .ContinueWith(x => x.Result, TaskScheduler.Default);
}

ただし、オーバーヘッドが発生するため、好きではありません。よりエレガントなソリューションが存在する可能性がありますか?

4

2 に答える 2

4

TaskCreationOptions.NET 4.6 の時点で、 calledにはオプションがありRunContinuationsAsynchronously、これにより、結果を設定するときに同期的に実行されるのではなく、すべての継続が非同期的に実行されることが保証されます。 TaskCompletionSourceそのオプションを提供するために、コンストラクターにオプションのTaskCreationOptionパラメーターがあります。

以前のバージョンの .NET を使用している場合は、コールバック アクションではなく、別の継続を追加するか、スレッド プール スレッドに結果を明示的に設定するなど、効率の低いハックを行う必要があります。

于 2016-01-07T15:18:43.540 に答える
0

正しく理解しているかどうかはわかりませんが、ブロックを回避するためにバックグラウンド タスクを明示的に作成できます。

await MyLibraryMethodAsync().ConfigureAwait(false);
await Task.Run(() => VeryLongRunningMethod());

次に、ConfigureAwait を省略することもできます。

await MyLibraryMethodAsync()
await Task.Run(() => VeryLongRunningMethod());

編集:あなたのコメントに基づいて:ライブラリの作成者として、スレッドのブロックを防ぎたい場合は、次を使用できます。

Task.Run(() => taskCompletionSource.SetResult(null));
于 2016-01-07T15:02:03.377 に答える