C# - AsyncLocal<> 値はスレッドセーフである必要がありますか?

okwaves2024-01-25  9

最近の PR レビューで、AsyncLocal<IDictionary<>>が有効かどうかについての質問が出ました。 ConcurrentDictionary<> を使用する必要があります。私の考えでは、AsyncLocal 値は複数のスレッドによって同時にアクセスされないため、その必要はありません。しかし、私は確信を持ちたいのです。

AsyncLocal に保持されているオブジェクトのスレッドセーフについて心配する必要がありますか?その理由、あるいはそうでない理由を説明してください。主張された答えを実証する単体テストに対するボーナス ポイント。

3

同時アクセスの有無 c発生するかどうかは、同期コンテキストによって異なります。

– GSerg

2020 年 9 月 3 日 21:46

1

AsyncLocal を使用すると、単一の変数のみを使用して、複数のタスクまたはスレッドがディクショナリへの個別の参照を持つことができます。それらが実際に異なるものであることは保証されません。ですから、心配する必要はありません。課題を見直してください。

– ハンス・パッサント

2020 年 9 月 3 日 21:54



------------------------

AsyncLocal は非同期制御フロー内のすべてのスレッドで使用できます。

次の例を見てみましょう:

using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp15
{
    class Program
    {

        static AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();

        static async Task Main(string[] args)
        {
            _asyncLocalString.Value = "TestString";

            var tasks = Enumerable.Range(0, 10).Select(_ => GetString());
            var results = await Task.WhenAll(tasks);
        }

        private static async Task<string> GetString()
        {
            await Task.Yield();
            return _asyncLocalString.Value + Thread.CurrentThread.ManagedThreadId;
        }
    }
}

ここでは、複数のスレッドが値「TestString」にアクセスできます。

さらに、値が非同期ローカルから取り出された後は、他の参照と同様に扱う必要があります。コールバックで使用される場合、呼び出し元に返される場合、クロージャでキャプチャされる場合など、他のスレッドに公開される可能性があります。参照型を使用すると、値が外部から変更され、競合状態が発生する可能性があります。

更新: 以下は辞書を使用した例です。

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

namespace ConsoleApp15
{
    class Program
    {

        static AsyncLocal<Dictionary<string, int>> _asyncLocalDict = new AsyncLocal<Dictionary<string, int>>();

        static async Task Main(string[] args)
        {
            _asyncLocalDict.Value = new Dictionary<string, int>();

            var tasks = Enumerable.Range(0, 10).Select(_ => Race());
            await Task.WhenAll(tasks);
        }

        private static async Task Race()
        {
            await Task.Yield();
            var dict = _asyncLocalDict.Value;
            if (!dict.ContainsKey("race")) dict["race"] = 0;
            dict["race"]++;
        }
    }
}

2

私はあなたを少し信じていますが、サンプルは実際にそれを証明していないようです。各タスクには、文字列の同等ではあるが異なるインスタンスが存在する可能性があるためです。実際に変更できるものを使用した方が良いかもしれません。

– E-リズ

2020 年 9 月 4 日 17:51

1

@E-Riz 同じ例を辞書に追加しました

– ジェイソン

2020 年 9 月 4 日 22:00

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。