C# Task と Task<T> の基本

 

初級編:C# Task と Task<T> の基本

1. Task の仕組みと非同期処理における役割

Taskは、C#の非同期プログラミングの基盤となるクラスで、非同期操作を表します。Taskは、時間のかかる処理をバックグラウンドで実行し、その処理が完了したときに結果を返すか、エラーを報告するために使われます。

Taskは、.NETのスレッドプールを使用して非同期タスクを実行するため、リソースを効率的に利用し、マルチスレッドプログラミングよりも簡単に並行処理を実装することが可能です。

基本的なTaskの例


using System;
using System.Threading.Tasks;

Task myTask = Task.Run(() =>
{
    // 時間のかかる処理
    Console.WriteLine("タスク実行中...");
    Task.Delay(2000).Wait(); // 2秒待機
    Console.WriteLine("タスク完了!");
});

await myTask;
Console.WriteLine("メイン処理終了");
            

上記の例では、Task.Runで新しい非同期タスクを開始し、2秒間待機した後に「タスク完了!」を表示しています。awaitを使用することで、タスクが完了するまで次の処理を待機します。

2. Task<T> で戻り値を扱う方法

Task<T>は、非同期操作の結果として型Tを返すタスクを表します。通常のTaskが結果を返さないのに対し、Task<T>は、例えばデータベースからのデータ取得など、非同期処理の結果を返すために使われます。

Task<T>の例


using System;
using System.Threading.Tasks;

async Task<int> CalculateSumAsync(int a, int b)
{
    await Task.Delay(1000); // 1秒待機
    return a + b;
}

int result = await CalculateSumAsync(5, 10);
Console.WriteLine($"計算結果: {result}");
            

この例では、CalculateSumAsyncメソッドが2つの整数を受け取り、非同期でその合計を計算してTask<int>として結果を返しています。awaitを使用してタスクが完了するのを待ち、結果を受け取ることができます。

3. Task.Run や Task.Delay の使い方

Task.RunTask.Delayは、非同期処理を簡単に始めるための基本メソッドです。

Task.Run

Task.Runは、新しいタスクをスレッドプールで非同期に実行します。バックグラウンドで動作させたい処理を実行したいときに使います。


Task myTask = Task.Run(() =>
{
    Console.WriteLine("非同期タスク実行中...");
    Task.Delay(1000).Wait(); // 1秒待機
    Console.WriteLine("非同期タスク完了");
});

await myTask;
            

Task.Runは、長時間実行されるタスクを別のスレッドで並行して処理したい場合に便利です。

Task.Delay

Task.Delayは、指定された時間だけ非同期で待機するために使用されます。通常、デバッグやタイミングの調整、非同期処理のシミュレーションなどに使われます。


await Task.Delay(2000); // 2秒間の非同期待機
Console.WriteLine("2秒後に実行");
            

上記の例では、2秒間待機した後にメッセージが表示されます。Task.Delayはタイミングの調整に便利です。

4. エラーハンドリング(例外処理)

非同期タスク内で例外が発生した場合、それを適切に処理するためには、try-catchブロックを使用します。TaskTask<T>内で発生した例外も、同期処理と同様に例外をキャッチして処理できます。

例:非同期処理での例外処理


async Task ProcessDataAsync()
{
    try
    {
        await Task.Run(() =>
        {
            throw new InvalidOperationException("例外が発生しました");
        });
    }
    catch (Exception ex)
    {
        Console.WriteLine($"エラー: {ex.Message}");
    }
}

await ProcessDataAsync();
Console.WriteLine("処理が完了しました");
            

この例では、Task.Run内で例外が発生した場合に、try-catchブロックで例外をキャッチし、エラーメッセージを表示しています。非同期タスクでも、例外はtry-catchを使ってハンドリングできます。

AggregateException

非同期タスクが複数の例外をスローする場合、これらはAggregateExceptionとして集約されます。AggregateExceptionは、複数の例外を一度に処理できる特別な例外クラスです。


async Task ProcessMultipleTasksAsync()
{
    try
    {
        Task task1 = Task.Run(() => throw new InvalidOperationException("Task 1 エラー"));
        Task task2 = Task.Run(() => throw new InvalidOperationException("Task 2 エラー"));

        await Task.WhenAll(task1, task2);
    }
    catch (AggregateException ex)
    {
        foreach (var innerEx in ex.InnerExceptions)
        {
            Console.WriteLine($"エラー: {innerEx.Message}");
        }
    }
}

await ProcessMultipleTasksAsync();
            

このコードでは、複数のタスクが例外をスローした場合にAggregateExceptionがキャッチされ、内部の例外を個別に処理しています。

まとめ

今回の記事では、TaskTask<T>の基本を学びました。Taskを使用して非同期処理を効率的に実装することで、バックグラウンドでの処理やスレッドの効率的な利用が可能になります。また、Task<T>を使えば非同期処理の結果を簡単に取得することができ、例外処理もtry-catchを通して行えます。次回は、複数のタスクを同時に処理する方法や、さらに高度な非同期プログラミングについて学んでいきましょう。

© 2024 C# Programming Blog. All rights reserved.