【发布时间】:2020-07-24 12:19:54
【问题描述】:
我很难找到关于 AsyncLocal<T> 所做工作的简单文档。
我写了一些测试,我认为告诉我答案是“是”,但如果有人能证实这一点,那就太好了! (特别是因为我不知道如何编写可以明确控制线程和延续上下文的测试......所以它们可能只是巧合!)
-
据我了解,
ThreadLocal将保证,如果您在不同的线程上,那么您将获得不同的对象实例。- 如果您正在创建和结束线程,那么您可能会在稍后再次重新使用该线程(从而到达一个“那个线程的”
ThreadLocal对象已经被使用过的线程)。李> - 但与
await的交互不太愉快。您继续使用的线程(即使.ConfigureAwait(true))不能保证与您开始使用的线程相同,因此您可能无法从另一端的ThreadLocal中取回相同的对象。
- 如果您正在创建和结束线程,那么您可能会在稍后再次重新使用该线程(从而到达一个“那个线程的”
-
相反,
AsyncLocal确实保证您将在await调用的任一侧获得相同的对象。
但我找不到任何地方实际上说 AsyncLocal 首先将获得一个特定于初始线程的值!
即:
- 假设您有一个实例方法 (
MyAsyncMethod),它在await调用的任一侧引用其类中的“共享”AsyncLocal字段 (myAsyncLocal)。 - 假设您获取该类的一个实例并多次并行调用该方法。 * 最后假设每次调用都恰好安排在不同的线程上。
我知道对于MyAsyncMethod 的每个单独调用,myAsyncLocal.Value 将在等待之前和之后返回相同的对象(假设没有重新分配它)
但是否保证每个调用首先会查看不同的对象?
如开头所述,我创建了一个测试来尝试自己确定这一点。以下测试始终通过
public class AssessBehaviourOfAsyncLocal
{
private class StringHolder
{
public string HeldString { get; set; }
}
[Test, Repeat(10)]
public void RunInParallel()
{
var reps = Enumerable.Range(1, 100).ToArray();
Parallel.ForEach(reps, index =>
{
var val = "Value " + index;
Assert.AreNotEqual(val, asyncLocalString.Value?.HeldString);
if (asyncLocalString.Value == null)
{
asyncLocalString.Value = new StringHolder();
}
asyncLocalString.Value.HeldString = val;
ExamineValuesOfLocalObjectsEitherSideOfAwait(val).Wait();
});
}
static readonly AsyncLocal<StringHolder> asyncLocalString = new AsyncLocal<StringHolder>();
static async Task ExamineValuesOfLocalObjectsEitherSideOfAwait(string expectedValue)
{
Assert.AreEqual(expectedValue, asyncLocalString.Value.HeldString);
await Task.Delay(100);
Assert.AreEqual(expectedValue, asyncLocalString.Value.HeldString);
}
}
【问题讨论】:
-
(我以为这是因为
Parallel.Foreach正在重用一些线程,因此这些线程看到的是以前初始化(但不再使用)的StringHolder?) -
不管怎样,你明白即使没有
But is it guaranteed that each of the invocations will be looking at different objects in the first place?是假的吗?因为如果您在Parallel.ForEach之前设置AsyncLocal,那么每次调用都会看到同一个对象(因此它们不是“不同的对象”)?还是我误会了? -
啊哈哈,是的,我明白你的意思了(并且同意提前设置 AsyncLocal 会导致所有孩子获得相同的对象,从而破坏彼此的断言)
-
但是您的示例很有趣 - programmer.help/blogs/… 声明
After the thread is used, it will return to the thread pool. The status of asynclocal < T > will be cleared and the previous value cannot be accessed- 您的代码示例似乎反驳。 -
@mjwills 我猜这是
Parallel.ForEach()的实现细节?也就是说,它实际上是在设置自己的个人“线程池”,而不是“放弃任何单独的线程”。因此,如果您“正常”使用线程,上面的引用是正确的?
标签: c# multithreading async-await thread-local-storage