tera1707
@tera1707

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

C#のTaskでデッドロックする場合/しない場合の違い

Q&A

Closed

解決したいこと

下記の実験コードで、デッドロックする/しないの違いの元を教えて頂きたいです。

下記のコードが、

  • ボタン押下時のハンドラの中でTask.Delay(1000).Wait();としたとき(①)と、Task.Runの中でawait Task.Delay(1000);したとき(②)はデッドロックしない。
  • ボタン押下時のハンドラから呼んでいる、中でawait Task.Delay(1000);をしているメソッドを.Wait()すると(③)、デッドロックする。

という動作をします。

using System.Threading.Tasks;
using System.Windows;

namespace WpfApp3
{
    public partial class MainWindow : Window
    {
        public MainWindow()=> InitializeComponent();

        private async Task FuncAsync()
        {
            await Task.Delay(1000);
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            // ①デッドロックしない
            Task.Delay(1000).Wait();

            // ②デッドロックしない
            Task.Run(async () =>
            {
                await Task.Delay(1000);
            }).Wait();

            // ③デッドロックする
            FuncAsync().Wait();
        }
    }
}

①も②も③も、見た目に全く同じことをしているように見えてしまうのですが、何の違いで、デッドロックする/しないが変化するのでしょうか?

環境

  • VisualStudio 2022
  • .NET6/WPF

試したことなど

以前、Taskの動きについて調べて記事を書かせていただいたとき(コチラ)には、下記のような理解をしていました。

image.png

.Wait()をして、呼び出し元のスレッドが止まってしまうため、
awaitをして呼び出し元のスレッドに処理を一旦戻そうとしたときに、
戻った先が止まっているためにその後動けなくなってデッドロックする、という理解です。

その理解で行くと、②については、Task.Runが.Wait()しているのでその時点で呼び出し元のUIスレッドが止まって、Task.Runの中のawaitの中で呼び出し元のメインスレッドに戻ろうとするので、デッドロックしそうなものだと思うのですが、、、

皆様の知恵を貸していただけると幸いです。よろしくお願いします。

3

2Answer

②については、Task.Runが.Wait()しているのでその時点で呼び出し元のUIスレッドが止まって、Task.Runの中のawaitの中で呼び出し元のメインスレッドに戻ろうとするので、デッドロックしそうなものだと思う

よくよく見ると,Task.Runの.Wait()でメインスレッドは止まってるけど、
その中で読んでいるawait Task.Delay(1000);は、ワーカスレッドから呼ばれているので、await時にメインスレッドに戻ろうとしないですね。

なので、やっぱり「メインスレッドでawaitすることになるメソッドを.Wait()すると、await時に止まってるメインスレッドに戻ろうとしてデッドロックする」ということですね。

質問してみて、頭整理できました、ありがとうございました。

1Like

TaskをWaitしてはいけない
async/awaitとTsak.Wait()を混在させると、必ずではないけどかなり簡単にデッドロックが発生する上にどのような条件で起こるのか把握しづらい、なのでTask.Wait()を一切使わないのが一番確実な解決策、という話です。

1Like

Comments

  1. @tera1707

    Questioner

    本当に簡単にデッドロックしてしまうので、Wait()使わないのがよさそうです。ありがとうございます!

Your answer might help someone💌