1. ホーム
  2. c#

[解決済み] プロミス型タスクでStartを呼び出してはいけない。

2022-02-17 18:54:20

質問

シンプルなwpfのデスクトップアプリケーションを作成しています。UIは、ボタンと.csファイルのコードだけを持っています。

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}

public void FunctionA()
{
    Task.Delay(5000).Start();
    MessageBox.Show("Waiting Complete");
}

しかし、驚くべきことに、この行は Task.Delay(5000).Start(); を投げています。 InvalidOperationException :

Start は、プロミス形式のタスクでは呼び出されない場合があります。

なぜこのような状態になるのか、どなたか教えていただけませんか?

解決方法は?

このエラーが発生するのは Task クラスは、あなたに渡す前にすでにタスクを開始しています。 を呼び出すのは Start は、コンストラクタを呼び出して作成したタスクで、タスクを作成したときに開始させないというやむを得ない理由がない限り、そうすべきではないでしょう。 Task.Run または Task.Factory.StartNew を作成し、新しい Task .

これで、あの厄介な Start . コードを実行すると、メッセージボックスが5秒後ではなく、すぐに表示されることがわかりますが、これはどうしたことでしょう?

さて。 Task.Delay は、5秒後に完了するタスクを与えるだけです。 スレッドの実行を5秒間停止させるわけではありません。 やりたいことは、そのタスクが終了した後に実行されるコードを用意することです。 それが ContinueWith はそのためのものです。 これは、与えられたタスクが完了した後に、いくつかのコードを実行することができます。

public void FunctionA()
{
    Task.Delay(5000)
    .ContinueWith(t => 
    {
        MessageBox.Show("Waiting Complete");
    });
}

これは期待通りの動作になります。

また、C# 5.0の await キーワードを使用すると、より簡単に連続性を追加することができます。

public async Task FunctionA()
{
    await Task.Delay(5000);
    MessageBox.Show("Waiting Complete");
}

ここで起こっていることの完全な説明はこの質問の範囲を超えていますが、最終的には前のメソッドと非常に似た振る舞いをするメソッドになります。メソッドを呼び出した5秒後にメッセージボックスが表示されますが、メソッド自体はどちらの場合も[ほとんど]すぐに返されます。 とはいえ await は非常に強力で、一見シンプルで簡単そうに見えるメソッドでも ContinueWith を直接使用します。 また、エラー処理も大幅に簡素化され、多くの定型的なコードを削除することができます。