AsyncLocal<> 值需要线程安全吗?

E-R*_*Riz 9 c# .net-core

最近的 PR 评论中出现了一个关于是否AsyncLocal<IDictionary<>>应该使用ConcurrentDictionary<>. 我的想法是不需要这样做,因为 AsyncLocal 值不会同时被多个线程访问。但我想确定一下。

我们是否需要担心 AsyncLocal 中保存的对象的线程安全性?请解释为什么或为什么不。证明所断言答案的单元测试的奖励积分。

Jas*_*son 8

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;
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

这里多个线程可以同时访问值“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"]++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述