【C#】WindowsFormsアプリで見る非同期処理

非同期処理は、プログラムが何か長い時間がかかる作業をしている間に、他の作業を同時に行えるようにする方法です。これは、例えばウェブページの読み込みやファイルのダウンロード、データベースからの情報の取得など、時間のかかる操作を行う際に非常に役立ちます。

非同期処理を使うことで、プログラムは次のように振る舞います。

  1. 通常の(同期)処理:一つの作業が終わるまで、次の作業に進めない。作業が遅い場合、全体のプログラムの動作が停止する可能性がある。
  2. 非同期処理:プログラムは重い作業を別のスレッド(仮想の作業スペース)で実行し、メインのスレッドでは他の作業を進める。つまり、重い作業が終わるのを待たずに、他の仕事も同時に進められる。

C#で非同期処理を使うには、asyncawaitというキーワードを使います。

同期と非同期の比較サンプル

サンプル動画

同期ボタンを押すと、UIがロックされているのがわかります
ロックとは、他のUI(ボタンなど)が押せない状態です
処理が終わるまで待たないといけません

非同期ボタンを押した時は、他のボタンも押すことができロックされないのがわかります

デザイン

2つのボタンと、2つのラベルを配置します
それぞれのボタンには、クリックした時のイベントハンドラを登録します

コード

namespace AsyncSample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void buttonBlocking_Click(object sender, EventArgs e)
        {
            // ボタンをクリックしたら、5秒間の間、UIはブロックされます
            Thread.Sleep(5000);
            labelBlocking.Text = "ブロックされました";
        }

        private async void buttonNonBlocking_Click(object sender, EventArgs e)
        {
            // ボタンをクリックしたら、5秒間の処理を非同期で実行し、UIはブロックされません
            labelNonBlocking.Text = "非同期処理中...";
            await Task.Delay(5000);
            labelNonBlocking.Text = "非同期処理完了";
        }
    }
}
  1. private async void buttonNonBlocking_Click(object sender, EventArgs e): このメソッドは、buttonNonBlockingという名前のボタンがクリックされたときに呼び出されます。このメソッドは非同期メソッドであり、asyncキーワードで修飾されています。
  2. labelNonBlocking.Text = "非同期処理中...";: labelNonBlockingという名前のラベルのテキストを「非同期処理中…」に設定します。これにより、ユーザーに非同期処理が進行中であることを通知します。
  3. await Task.Delay(5000);: Task.Delayメソッドを使用して、非同期で5秒間の待機を行います。これにより、UIはブロックされませんが、5秒間の間に他の操作が実行されないことを保証します。この待機が終了すると、次の行が実行されます。
  4. labelNonBlocking.Text = "非同期処理完了";: 待機が終了した後、labelNonBlockingのテキストを「非同期処理完了」に設定し、非同期処理が正常に完了したことをユーザーに通知します。

Thread.Sleep(5000)await Task.Delay(5000) の違い

Thread.Sleep(5000)await Task.Delay(5000) は、両方とも一定の時間(ここでは5000ミリ秒、つまり5秒)の間待機するために使用されるコードフラグメントですが、その動作と使用方法には重要な違いがあります。

  1. Thread.Sleep(5000):
    • Thread.Sleep メソッドは、カレントスレッド(実行中のスレッド)を指定された時間だけ一時停止します。
    • これは同期的な操作であり、カレントスレッドが5秒間ブロックされ、その間他のタスクやスレッドは処理できません。したがって、UIスレッドやメインスレッドなどのスレッドで Thread.Sleep を使用すると、アプリケーションの応答性が低下する可能性があります。非UIスレッドでの使用は影響が少ないですが、他のスレッドが同じリソースにアクセスする場合には注意が必要です。
  2. await Task.Delay(5000):
    • Task.Delay メソッドは、非同期的な待機を提供し、非ブロッキングです。
    • await キーワードと一緒に使用されることが一般的で、Task.Delay の結果を待機し、その間に他のタスクやスレッドが処理できるようにします。つまり、カレントスレッドがブロックされないため、アプリケーションの応答性が維持されます。
    • このアプローチは、UIスレッドやメインスレッドでの使用に適しており、非同期プログラミングパターンと組み合わせて、アプリケーションのスムーズな動作を確保するのに役立ちます。

総括すると、Thread.Sleep は同期的でブロッキングな待機方法であり、Task.Delay は非同期で非ブロッキングな待機方法です。非同期プログラミングのコンテキストでは、Task.Delay を使用することをお勧めします。

非同期の時、同じボタンを連続で押されないようにしたい

サンプル動画

非同期ボタンをクリックするとボタンが無効化されます
処理が終わると有効化されます

コード

private async void buttonNonBlocking_Click(object sender, EventArgs e)
{
    buttonNonBlocking.Enabled = false;
    // ボタンをクリックしたら、5秒間の処理を非同期で実行し、UIをブロックしない
    labelNonBlocking.Text = "非同期処理中...";
    await Task.Delay(5000);
    labelNonBlocking.Text = "非同期処理完了";
    buttonNonBlocking.Enabled = true;
}

このコードは、C#のWindows Formsアプリケーションで使用されることを想定しています。コードの目的は、ボタンをクリックした際にUIをブロックせずに非同期処理を実行することです。

以下はコードの詳細な説明です:

  1. private async void buttonNonBlocking_Click(object sender, EventArgs e): このメソッドは、buttonNonBlockingという名前のボタンがクリックされたときに呼び出されます。このメソッドは非同期メソッドであり、asyncキーワードで修飾されています。
  2. buttonNonBlocking.Enabled = false;: ボタンをクリックした直後、buttonNonBlockingボタンを無効にします。これにより、ユーザーが連続してボタンをクリックして処理を開始するのを防ぎます。
  3. labelNonBlocking.Text = "非同期処理中...";: labelNonBlockingという名前のラベルのテキストを「非同期処理中…」に設定します。これにより、ユーザーに非同期処理が進行中であることを通知します。
  4. await Task.Delay(5000);: Task.Delayメソッドを使用して、非同期で5秒間の待機を行います。これにより、UIはブロックされませんが、5秒間の間に他の操作が実行されないことを保証します。この待機が終了すると、次の行が実行されます。
  5. labelNonBlocking.Text = "非同期処理完了";: 待機が終了した後、labelNonBlockingのテキストを「非同期処理完了」に設定し、非同期処理が正常に完了したことをユーザーに通知します。
  6. buttonNonBlocking.Enabled = true;: 最後に、buttonNonBlockingボタンを再度有効にし、ユーザーが再びボタンをクリックできるようにします。

このコードは、UIを非同期的に更新し、ブロックしないようにする一般的なパターンを示しています。ユーザーがボタンをクリックしても、アプリケーションは応答性を維持し、非同期処理をバックグラウンドで実行します。

作成したメソッド中で実行したい

上記サンプルをメソッド化しました

private async void buttonNonBlocking_Click(object sender, EventArgs e)
{
    labelNonBlocking.Text = "非同期処理中...";

    await WaitMethodAsync();

    labelNonBlocking.Text = "非同期処理完了";
}

private async Task WaitMethodAsync()
{
    await Task.Delay(5000);
}

戻り値を取得

private async void buttonNonBlocking_Click(object sender, EventArgs e)
{
    // ボタンをクリックしたら、5秒間の処理を非同期で実行し、UIをブロックしない
    labelNonBlocking.Text = "非同期処理中...";
    var result = await WaitMethodAsync(); // 非同期メソッドを使用
    labelNonBlocking.Text = result;
}

private async Task<string> WaitMethodAsync()
{
    await Task.Delay(5000); // 非同期待機を使用してUIスレッドをブロックしない

    return "おわり";
}

C#,イベント,非同期

Posted by hidepon