在这个问题由斯蒂芬·克利里公认的答案说,LogicalCallContext无法与异步正常工作。 他还发布了关于它在这个 MSDN线程。
LogicalCallContext保持一个Hashtable存储发送到CallContext.LogicalGet / SetData的数据。 它不仅会哈希表的浅表副本。 所以,如果你存储一个可变对象在里面,不同的任务/线程将看到对方的变化。 这就是为什么斯蒂芬·克利里的例子NDC计划(发布于该MSDN线程)无法正常工作。
但是AFAICS,如果你只存储在Hashtable不可改变的数据(比如用不可变的集合 ),应该干活,而让我们实现一个NDC。
然而,斯蒂芬·克利里也该接受的回答说:
CallContext中不能被用于此目的。 微软已经明确建议反对使用CallContext中的任何东西,除了远程处理。 更重要的是,逻辑CallContext中不懂得如何异步方法提前返回,并在稍后继续。
不幸的是,链接到微软的建议是向下(网页未找到)。 所以,这是为什么不建议我的问题? 为什么我不能以这种方式使用LogicalCallContext? 这是什么意思就是说它不理解异步方法? 从来电者的POV他们只是方法返回的任务,不是吗?
ETA:又见这个问题等 。 在那里,由斯蒂芬·克利里回答说:
你可以使用CallContext.LogicalSetData和CallContext.LogicalGetData,但我建议你不这样做,因为他们不支持任何形式的“克隆”,当您使用简单的并行
这似乎支持我的情况。 所以,我应该能够建立一个NDC,这实际上是我需要的,只是不适合log4net的。
我写了一些示例代码和它似乎工作,但仅仅是测试并不总是赶并发错误。 所以,因为有这些其他职位的提示,这可能无法正常工作,我仍然在问:是这种方法有效吗?
ETA:当我从下面的答案)运行斯蒂芬提出的摄制,我没有得到错误的答案,他说我会,我得到正确的答案。 即使他说,“这里LogicalCallContext值始终为‘1’”,我总是得到0的正确值是这可能是由于竞争状态? 无论如何,我仍未重现我自己的电脑上的任何实际问题。 下面是我运行的确切代码; 它打印唯一的“真”在这里,在这里斯蒂芬说,它应打印“假”至少一些的时间。
private static string key2 = "key2";
private static int Storage2 {
get { return (int) CallContext.LogicalGetData(key2); }
set { CallContext.LogicalSetData(key2, value);}
}
private static async Task ParentAsync() {
//Storage = new Stored(0); // Set LogicalCallContext value to "0".
Storage2 = 0;
Task childTaskA = ChildAAsync();
// LogicalCallContext value here is always "1".
// -- No, I get 0
Console.WriteLine(Storage2 == 0);
Task childTaskB = ChildBAsync();
// LogicalCallContext value here is always "2".
// -- No, I get 0
Console.WriteLine(Storage2 == 0);
await Task.WhenAll(childTaskA, childTaskB);
// LogicalCallContext value here may be "0" or "1".
// -- I always get 0
Console.WriteLine(Storage2 == 0);
}
private static async Task ChildAAsync() {
var value = Storage2; // Save LogicalCallContext value (always "0").
Storage2 = 1; // Set LogicalCallContext value to "1".
await Task.Delay(1000);
// LogicalCallContext value here may be "1" or "2".
Console.WriteLine(Storage2 == 1);
Storage2 = value; // Restore original LogicalCallContext value (always "0").
}
private static async Task ChildBAsync() {
var value = Storage2; // Save LogicalCallContext value (always "1").
Storage2 = 2; // Set LogicalCallContext value to "2".
await Task.Delay(1000);
// LogicalCallContext value here may be "0" or "2".
Console.WriteLine(Storage2 == 2);
Storage2 = value; // Restore original LogicalCallContext value (always "1").
}
public static void Main(string[] args) {
try {
ParentAsync().Wait();
}
catch (Exception e) {
Console.WriteLine(e);
}
所以我重申问题是,什么(如果有的话)是错的,上面的代码?
此外,当我在看的CallContext.LogicalSetData代码,它调用Thread.CurrentThread.GetMutableExecutionContext()和修改了。 而GetMutableExecutionContext说:
if (!this.ExecutionContextBelongsToCurrentScope)
this.m_ExecutionContext = this.m_ExecutionContext.CreateMutableCopy();
this.ExecutionContextBelongsToCurrentScope = true;
而CreateMutableCopy最终是否LogicalCallContext的哈希表保存用户提供的数据的浅表副本。
所以,试图理解为什么这个代码不斯蒂芬的工作,是不是因为ExecutionContextBelongsToCurrentScope有错误的价值有时会? 如果是这样的话,也许我们可以注意到,当它 - 通过看,要么当前任务ID或当前线程ID已经改变 - 和手动存储在我们不变的结构,通过螺纹+任务ID键入不同的值。 (有使用这种方法的性能问题,例如死任务数据的保留,但除此之外,将它的工作?)