おれんじりりぃぶろぐ

きっと何者にもなれないエンジニアのブログ

UnityにおけるAwaitAsyncを使った非同期実装について④

概略

前回までに最新Unityにおけるマルチスレッドプログラミングについて、さらにその時直面する問題点について紹介してきました。今回は、Task、awaitを使ったマルチスレッドでの排他制御について触れていきたいと思います。 今回も特にUnityに特化した情報はありません。

実装方法

複数のスレッドでから同時にアクセスされる可能性があるリソースにはlock文を使い排他制御をします。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour
{

    private int sum = 0; // 同時にアクセスされる可能性があるデータ
    private object syncObject = new object();

    private async void Start()
    {
        sum = 0;
        
        List<Task> t = new List<Task>();
        t.Add(TestAsyncMethod());
        t.Add(TestAsyncMethod());
        t.Add(TestAsyncMethod());
        await Task.WhenAll(t);
        
        Debug.Log($"sum: {sum}");
    }

    private async Task TestAsyncMethod()
    {
        await Task.Run(() =>
        {
            lockSum();
        });
    }

    // だめな例
    private void nolockSum()
    {
        for (int i = 1; i <= 1000; i++)
        {
            sum += i * i;
        }
    }
    

    private void lockSum()
    {
        for (int i = 1; i <= 1000; i++)
        {
            // 同時にアクセスされる可能性があるデータ
            lock(syncObject) { sum += i * i; }
        }
    }
}

await句の中ではlock文を使うことはできません。ロックをかけたスレッドとロックを解除するスレッドが異なる可能性があるからです。 nolockSum()の場合は、sumに対して競合が発生して毎回異なる結果になってしまいます。

まとめ

AsyncAwaitが導入されようとも漏れなく排他制御を行うのは難しい(´・_・`)