4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

再入門C#:デッドロック

Posted at

コンカレントプログラミングの本を読んでいると、必ずデッドロックの話がでてくるので、実際にデッドロックするのか試したくなった。

デッドロックするコード

DeadLock() メソッドをシングルスレッドしか許されていない箇所から呼び出すとデッドロックが起こる。例えばWPFのUIスレッドから、これを呼び出すとデッドロックが起こる。理由は task.Wait() を呼び出すとこのスレッドのところで、待ちが発生する。一方、WaitAsAsync() メソッドの方では、await で、Task.Delay() を呼んでいる。await のセクションに入るときに現在のスレッドのコンテキストを保存する。await の箇所が終わると、そのコンテキストをリストアするのだが、その時にそのコンテキストは、task.Wait() によってロックがかかっているために、デッドロックになる。

        private async Task WaitAsAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(1)); 
        }

        private void DeadLock()
        {
            Task task = WaitAsAsync();
            task.Wait();
        }

WPF アプリ

WPFは触ったことないけど、師匠のブログを読んで簡単なアプリを作ってみた。ボタンをおしていくと5回目で、Thread.Sleep() がかかり、10回目でこのデッドロックのロジックが呼ばれる。

MainWindow.xaml.cs
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new Prefecture();
        }

        private async Task WaitAsAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(1)); 
        }

        private void DeadLock()
        {
            Task task = WaitAsAsync();
            task.Wait();
        }

        private int count = 0; 
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var button = (Button) sender;
            button.Content = string.Format($"{++count} times.");
            if (count == 4)
            {
                button.Content = string.Format($"Sleep 10 sec....");
            }
            if (count == 4)
            {
                Thread.Sleep(TimeSpan.FromSeconds(10));
            }

            if (count == 10)
            {
                button.Content = string.Format($"DeadLock....");
            }
            if (count == 11)
            {
                DeadLock();
            }
        }
    }
MainWindow.xaml
<Window x:Class="WPFSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFSample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition>
            </RowDefinition>
            <RowDefinition>
            </RowDefinition>
        </Grid.RowDefinitions>

    <ComboBox ItemsSource="{Binding Path=Data}" x:Name="comboBox" Grid.Row ="0" Grid.Column="0"/>
    <Button Content="0回" Grid.Row="1" Grid.Column="0" Click="Button_Click"/>
    </Grid>
</Window>

デッドロックに行く直前なら、プルダウンなどの操作できるが、一旦行ってしまうと何もできなくなる。

image.png

操作不能

image.png

回避方法

ConfigureAwait() を使うと、コンテキストのリストアをするか否かを制御できる。falseにすると、コンテキストをリストアして元のスレッドに戻ろうとしない(違うスレッドを使う)ので、デッドロックが起こらなくなる。

        private async Task WaitAsAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
        }
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?