在TPL清理CallContext中 [英] Cleaning up CallContext in TPL
问题描述
根据我是否使用异步/等待基于code或TPL基于code,我收到有关的逻辑清理两种不同的行为 CallContext中
。
Depending on whether I'm using async/await based code or TPL based code, I'm getting two different behaviors regarding the clean-up of logical CallContext
.
我可以设置和清晰的逻辑 CallContext中
正是我希望如果我用下面的异步/等待code:
I can set and clear logical CallContext
exactly as I expect if I use the following async/await code:
class Program
{
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
以上输出以下内容:
The above outputs the following:
{地方= Task.Run,ID = 9,味精=世界}结果
{广场=主,ID = 8,味精=}
{ Place = Task.Run, Id = 9, Msg = world }
{ Place = Main, Id = 8, Msg = }
注意消息=
这表明 CallContext中
在主线程中被释放,是空的。
Notice the Msg =
which indicates that CallContext
on the main thread has been freed and is empty.
但是,当我切换到纯TPL / TAP code,我不能达到同样的效果...
But when I switch to pure TPL / TAP code I can't achieve the same effect...
class Program
{
static Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return result;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
以上输出以下内容:
The above outputs the following:
{地方= Task.Run,ID = 10,味精=世界}结果
{广场=主,ID = 9,味精=世界}
{ Place = Task.Run, Id = 10, Msg = world }
{ Place = Main, Id = 9, Msg = world }
有什么我可以做要挟TPL自由的逻辑 CallContext中
相同的方式,异步/的await code呢?
Is there anything I can do to coerce TPL to "free" the logical CallContext
the same way as the async/await code does?
我不感兴趣的替代品 CallContext中
。
I am not interested in alternatives to CallContext
.
我希望能得到上面的TPL / TAP code固定,这样我可以在面向.NET 4.0框架项目中使用。如果不是在.NET 4.0中可能的话,我还是好奇,如果它可以在.NET 4.5来完成。
I'm hoping to get the above TPL/TAP code fixed so that I can use it in projects targeting the .net 4.0 framework. If that is not possible in .net 4.0, I'm still curious if it can be done in .net 4.5.
推荐答案
在异步
方法 CallContext中
是复制上写:
当异步方法启动时,它会通知其逻辑调用上下文激活写入时复制行为。这意味着当前的逻辑调用上下文实际上并没有改变,但它标记,以便如果你的code不叫 CallContext.LogicalSetData code>,逻辑调用上下文数据复制进入一个新的当前逻辑调用上下文前被改变。
When an async method starts, it notifies its logical call context to activate copy-on-write behavior. This means the current logical call context is not actually changed, but it is marked so that if your code does call
CallContext.LogicalSetData
, the logical call context data is copied into a new current logical call context before it is changed.
这意味着,在你的异步
版本的 CallContext.FreeNamedDataSlot(你好)
延续冗余,因为即使没有它:
That means that in your async
version the CallContext.FreeNamedDataSlot("hello")
continuation is redundant as even without it:
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Console.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
}
的 CallContext中
在主
将不包含你好
槽:
{地方= Task.Run,ID = 3,消息=世界}结果
{广场=主,ID = 1,消息=}
{ Place = Task.Run, Id = 3, Msg = world }
{ Place = Main, Id = 1, Msg = }
在TPL相当于外 Task.Run
(这应该是 Task.Factory.StartNew $ C $所有code C>为
Task.Run
在.NET 4.5中添加)与运行在完全相同 CallContext中$ C $在同一线程上C>
。如果你想清理它,你需要做的,关于这方面(而不是在延续):
In the TPL equivalent all code outside the Task.Run
(which should be Task.Factory.StartNew
as Task.Run
was added in .Net 4.5) runs on the same thread with the same exact CallContext
. If you want to clean it you need to do that on that context (and not in the continuation):
static Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Factory.StartNew(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
CallContext.FreeNamedDataSlot("hello");
return result;
}
您甚至可以抽象一个范围出它确保你总是好自己清理:
You can even abstract a scope out of it to make sure you always clean up after yourself:
static Task DoSomething()
{
using (CallContextScope.Start("hello", "world"))
{
return Task.Factory.StartNew(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
}
}
使用:
public static class CallContextScope
{
public static IDisposable Start(string name, object data)
{
CallContext.LogicalSetData(name, data);
return new Cleaner(name);
}
private class Cleaner : IDisposable
{
private readonly string _name;
private bool _isDisposed;
public Cleaner(string name)
{
_name = name;
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
CallContext.FreeNamedDataSlot(_name);
_isDisposed = true;
}
}
}
这篇关于在TPL清理CallContext中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!