当前线程有一个可以在Thread.CurrentThread.ManagedThreadId
访问的线程ID。线程内的所有同步调用显然都共享此ID。
但是如果调用异步方法,则异步调用会显示不同的托管线程ID。
public void ThreadTest()
{
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var asyncThreadId = GetThreadIdAsync().Result;
currentThreadId.ShouldEqual(asyncThreadId); // fails
}
private async Task<int> GetThreadIdAsync()
{
return await Task.Run(() => Thread.CurrentThread.ManagedThreadId);
}
我想某种方式能够看到第二种方法与第一种方法属于同一工作单元,如果不是ManagedThreadId
,那么就是类似的方法。他们有共同的背景吗?
答案 0 :(得分:3)
.NET有ExecutionContext
流经异步调用(实际上它在使用几乎所有线程API时流动,例如线程池.APM,定时器 - 除了Unsafe*
线程池方法或在使用ExecutionContext.SuppressFlow()
)暂停时。
您可以使用CallContext
,CorrelationManager
或AsyncLocal<T>
将数据添加到ExecutionContext
。
请注意,向执行上下文添加数据会强制运行时在每次流动时克隆上下文,可能代价高昂。
CallContext
调用上下文就像一个与执行上下文一起流动的字典。
CallContext.LogicalSetData("MyData", 1);
await Task.Run(() => Console.WriteLine(CallContext.LogicalGetData("MyData"))); // prints 1
CorrelationManager
,主要用于跟踪,使用CallContext
并允许存储两个特定值,ActivityId
(a Guid
)和LogicalOperationStack
。例如,WCF在分派服务方法时自动设置活动ID,甚至可以跨机器传输ID。
Trace.CorrelationManager.ActivityId = Guid.NewGuid();
await Task.Run(() => Console.WriteLine(Trace.CorrelationManager.ActivityId));
AsyncLocal<T>
此API是在.NET 4.6中引入的,与CallContext
相比有几个优点:
示例:
static local = new AsyncLocal<int>(); // you can save this local anywhere
var tasks = Enumerable.Range(0, 10).Select(i => Task.Run(async () =>
{
local.Value = i;
await Task.Run(() => Console.WriteLine($"{i} = {local.Value}"));
}));
await Task.WhenAll(tasks);