web-dev-qa-db-ja.com

Task.RunSynchronouslyとは何ですか?

方法は何だろう?どのようなシナリオでこの方法を使用できますか。

私の最初の考えは、RunSynchronouslyは非同期メソッドを呼び出し、.wait()のようなデッドロックの問題を引き起こすことなく、それを同期的に実行することです。

ただし、 [〜#〜] msdn [〜#〜] によると、

通常、タスクはスレッドプールスレッドで非同期に実行され、呼び出しスレッドをブロックしません。 RunSynchronously()メソッドを呼び出して実行されるタスクは、現在のTaskSchedulerに関連付けられ、呼び出し元のスレッドで実行されます。ターゲットスケジューラが呼び出しタスクでこのタスクの実行をサポートしていない場合、タスクはスケジュールに従って実行されるようにスケジュールされ、呼び出しスレッドはタスクの実行が完了するまでブロックします

タスクが呼び出しスレッドで実行される場合、ここにTaskSchedulerが必要なのはなぜですか?

13
ValidfroM

RunSynchronouslyは、タスクをいつ開始するかの決定を現在のタスクスケジューラ(または引数として渡されたスケジューラ)に委任します。

なぜそこにあるのかはわからない(おそらく内部またはレガシー使用)が、。NETの現在のバージョンで有用なユースケースを考えるのは難しい。 @ Fabjanはコメントに可能な説明があります 質問に対して。

RunSynchronouslyは、スケジューラーに同期的に実行するよう要求しますが、スケジューラーはヒントを無視してスレッドプールスレッドで実行することができ、現在のスレッドは完了するまで同期的にブロックします。

スケジューラは現在のスレッドで実行する必要はなく、すぐに実行する必要はありませんが、一般的なスケジューラ(ThreadPoolTask​​Schedulerおよび一般的なUIスケジューラ)で起こると思います。

RunSynchronouslyは、タスクが既に開始されている場合、または完了/障害が発生している場合も例外をスローします(つまり、非同期メソッドで使用できないことを意味します)。

このコードは異なる動作を明確にするかもしれません:

WaitおよびResultはタスクをまったく実行せず、現在のスレッドでタスクの完了を待機し、完了までブロックするため、比較する場合はStartおよびWaitRunSynchronously

class Scheduler : TaskScheduler
{
    protected override void QueueTask(Task task) => 
        Console.WriteLine("QueueTask");

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        Console.WriteLine("TryExecuteTaskInline");

        return false;
    }

    protected override IEnumerable<Task> GetScheduledTasks() => throw new NotImplementedException();
}

static class Program
{
    static void Main()
    {
        var taskToStart = new Task(() => { });
        var taskToRunSynchronously = new Task(() => { });

        taskToStart.Start(new Scheduler());
        taskToRunSynchronously.RunSynchronously(new Scheduler());
    }
}

StartまたはRunSynchronouslyを試してコメントを付けてコードを実行すると、Startがインラインで試行し、失敗した場合にRunSynchronouslyがタスクをスケジューラに試行してキューに入れることがわかります( falseを返します)、キューに入れるだけです。

8

まず、このコードを見てみましょう。

_public async static Task<int> MyAsyncMethod()
{
   await Task.Delay(100);
   return 100;
}

//Task.Delay(5000).RunSynchronously();                        // bang
//Task.Run(() => Thread.Sleep(5000)).RunSynchronously();     // bang
// MyAsyncMethod().RunSynchronously();                      // bang

var t = new Task(() => Thread.Sleep(5000));
t.RunSynchronously();                                     // works
_

この例では、次のタスクでRunSynchronouslyを呼び出そうとしました。

  • 結果とともに他のタスクを返します(約束タスク)
  • 「ホット」タスクです
  • _async await_によって作成された別のpromiseタスク
  • デリゲート付きの「コールド」タスク

作成後にどのようなステータスになりますか?

  • アクティベーション待ち
  • WaitingToRun
  • WaitingForActivation
  • 作成した

すべての「ホット」タスクは、ステータスWaitingForActivationor WaitingToRunで作成され、タスクスケジューラに関連付けられます。

メソッドRunSynchronouslyは、デリゲートを含み、ステータスがCreatedである 'cold'タスクの操作方法のみを知っています。

結論:

メソッドRunSynchronouslyは、「ホット」タスクがなかった場合、またはそれらが広範囲に使用されておらず、特定の目的のために作成された場合におそらく表示されていました。

カスタムTaskSchedulerを使用した「コールド」タスクが必要な場合に使用したい場合があります。

「ホット」タスクを同期的に実行するには(これを避ける必要があります)、task.GetAwaiter().GetResult()を使用できます。ボーナスとして、AggregateExceptionのインスタンスではなく、元の例外を返します。

3
Fabjan