在异步编程中,处理异步操作之间的数据流转是一个比较常用的操作。C#
异步编程提供了一个强大的工具来解决这个问题,那就是AsyncLocal
。它是一个线程本地存储的机制,可以在异步操作之间传递数据。它为我们提供了一种简单而可靠的方式来共享数据,而不必担心线程切换或异步上下文的变化。本文我们将探究AsyncLocal
的原理和用法,并进行相关源码解析。探讨它如何在异步操作之间实现数据的流转,以及它是如何在底层工作的。
上面我们提到了AsyncLocal
可以在异步操作间传递数据,我们在之前的文章<研究c#异步操作async awAIt状态机的总结>一文中提到过异步操作会涉及到线程切换的问题,接下来通过Task来模拟一个简单异步示例,来看一下它的工作方式是什么样的,以便加深对它的理解,先看一下示例
AsyncLocal<Person> context = new AsyncLocal<Person>();
context.Value = new Person { Id = 1, Name = "张三" };
Console.WriteLine($"Main之前:{context.Value.Name},ThreadId={Thread.CurrentThread.ManagedThreadId}");
await Task.Run(() =>
{
Console.WriteLine($"Task1之前:{context.Value.Name},ThreadId={Thread.CurrentThread.ManagedThreadId}");
context.Value.Name = "李四";
Console.WriteLine($"Task1之后:{context.Value.Name},ThreadId={Thread.CurrentThread.ManagedThreadId}");
});
await Task.Run(() =>
{
Console.WriteLine($"Task2之前:{context.Value.Name},ThreadId={Thread.CurrentThread.ManagedThreadId}");
context.Value.Name = "王五";
Console.WriteLine($"Task2之后:{context.Value.Name},ThreadId={Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine($"Main之后:{context.Value.Name},ThreadId={Thread.CurrentThread.ManagedThreadId}");
在上面的示例中,我们创建了一个AsyncLocal
实例,并赋值了一个Person
对象,然后我们创建了两个Task
,分别执行了两个异步操作,并分别修改了AsyncLocal
中的Person
对象的值,分别在执行异步之前执行异步过程中和执行异步之后打印值来观察变化,执行程序输出结果如下
Main之前:张三,ThreadId=1
Task1之前:张三,ThreadId=4
Task1之后:李四,ThreadId=4
Task2之前:李四,ThreadId=6
Task2之后:王五,ThreadId=6
Main之后:王五,ThreadId=6
从输出结果来看,虽然我们在异步中修改了AsyncLocal
里Person
对象的值,并且也发生了线程切换。但是它可以在异步操作之间的数据共享和传递,使得我们在异步间进行的数据就和在一个线程里操作数据一样,让我们可以忽略掉其实已经发生了多次线程切换。
通过上面的示例,我们发现AsyncLocal
确实可以实现异步之间的数据共享和传递,那么它是如何实现的呢?接下来,我们通过先查看AsyncLocal
涉及到的相关源码来探究一下。想弄明白它的流转问题,需要研究两个大方向,一个是AsyncLocal
的本身实现,一个是AsyncLocal
的流转涉及到的异步或者多线程相关这里涉及到的主要是Task
和线程池
里的相关实现。由于异步相关涉及到了一整个体系,所以但看某一点的时候可能不太容易理解,我们先从AsyncLocal
本身入手,然后从Task
入手,最后从线程池
入手,逐步探究AsyncLocal
如何进行数据流转的。但是仍然希望能在阅读本文之前先了解一下设计到该话题的相关文章,先对整体有一个整体的把握
• AsyncLocal细节的文章可以看一下黑洞大佬的<浅析 .NET 中 AsyncLocal 的实现原理>
• 异步状态机介绍可以看一下我之前的文章<研究c#异步操作async await状态机的总结>
虽然强烈建议先看一下上面推荐的文章,但是在这里我们还是简单介绍一下AsyncLocal
的实现,所以这里我们简单介绍一下,方便大家能直观的看到。其实涉及到的比较简单,就是看一下AsyncLocal
里涉及到关于Value的操作即可[点击查看AsyncLocal.Value源码