[C#]Taskを使って非同期の処理をする方法とハマったところ

C#

C#で非同期処理をしたいときのTaskの使い方について書いています。
AzureFunctionでTask.Runを使いましたがハマった点があったので、それについても記載しています。

Taskで非同期処理する

Taskクラスについての公式資料はこちら

Task.Runを使う

Task.Runで渡す引数で、ラムダ式を使って、やりたい処理を書いておくと非同期処理してくれます。

  Task.Run(() => {
      var name = req.Query["name"];
      Console.WriteLine($"名前は{name}です。");
  });

staticなメソッドを作って、呼び出すことも可能です。

  Task.Run(() => TaskMethod());

Taskをnewで作ってstartする

newでTaskを作って、引数にはstaticメソッドを渡します。
.Start()を呼ぶと作ったタスクが実行できます。

  var newTask = new Task(TaskMethod);
  newTask.Start();

Taskを使って、並列的に複数処理を行う

複数回呼ぶとそれぞれ、処理をしてくれます。

  Task.Run(() => {
      Thread.Sleep(1000);
      Console.WriteLine($"thread test1");
  });
  Task.Run(() => {
      Thread.Sleep(1000);
      Console.WriteLine($"thread test2");
  });
  Task.Run(() => {
      Thread.Sleep(1000);
      Console.WriteLine($"thread test3");
  });
  var newTask = new Task(TaskMethod);
  newTask.Start();

注意したいところ

ちょっとハマった注意したい点ですが、、
再現するためにAzureFunctionで下記のような処理を書いてみました。

[FunctionName("ThreadTest")]
public static IActionResult ThreadTest(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "thead/test")] HttpRequest req
)
{
    Task.Run(() => {
        Thread.Sleep(3000);
        var name = req.Query["name"];
        Console.WriteLine($"名前は{name}です。");
    });
    Task.Run(() => TaskMethod());
    return new OkObjectResult("thread test!!");
}

getリクエストのqueryパラメータでnameを渡しているものを
3秒待った後にコンソールに表示としていますが、上の処理はうまくいきません。

原因はmainの処理が終わって、OkObjectResultが返っているので、既にreqが3秒後には消えてるからだと思いました。
なので、下記のように書くとうまく行きます。

[FunctionName("ThreadTest2")]
public static IActionResult ThreadTest2(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "thead/test2")] HttpRequest req
)
{
    var name = req.Query["name"];
    Task.Run(() => {
        var name = req.Query["name"];
        Thread.Sleep(3000);
        Console.WriteLine($"名前は{name}です。");
    });
    return new OkObjectResult("thread test!!");
}

nameを先に取るようにします。

こんな感じで、mainの処理が終わると、mainだけで使っていたものは消え去ってしまうので、mainで使っているものをTaskで使用して処理を書くときにはオブジェクトが消えることを意識して、先にCloneまたは新しくObjectをnewしておいた方が無難なようです。

サンプル

今回テストしたコードをgithubのこちらにあげてます。
DefaultController.csに書いてみました。

コメント

タイトルとURLをコピーしました