并行:ThreadStatic 属性的生命周期 (.NET)

Parallel: lifetime of ThreadStatic properties (.NET)

我想了解在 .NET 中使用并行处理时 ThreadStatic 数据何时被清除。

考虑以下(大幅缩减)代码:

我的背景class

public class AppContext
{
    [ThreadStatic]
    private static Person _person;

    public Person Shopper
    {
        get => AppContext._person;
        set => AppContext._person = value;
    }
}

并行处理大量对象

var response = await Get100ItemsToProcess().ConfigureAwait(false);
var singleContext = new AppContext();

Parallel.ForEach(response.items, new ParallelOptions(), i =>
{
    // Set all properties on the singleContext for this thread.
    singleContext.Shopper = new Shopper { Name = ...., etc}
    ....

    ProcessItem(i);

    // Dispose of any IDisposable properties on the singleContext
    ....
});

注:

所以,我的理解(如有错误请指正)是:

因此我的问题是:这些属性何时从内存中删除?它们是在线程返回到 ThreadPool 时(大概是在 Parallel.ForEach 完成时)清理的,还是在稍后将线程分配给不同的 AppDomain 时清理的?我问是因为其中一些属性可能会消耗大量内存,我想确保它们占用内存的时间不会超过他们需要的时间。我不太喜欢在每次循环迭代结束时将它们显式设置为 null 的想法...

ThreadStatic是线程专有存储space.

lifetime of ThreadStatic properties

ThreadStatic是线程专有存储space,所以ThreadStatic属性对象的生命周期就是线程的生命周期。

when we create a Parallel.ForEach loop, it gets assigned a handful of threads from the Thread Pool

Parallel 比这更复杂。它可以在执行期间根据需要调整它使用的线程数。线程可以同时 "enter" 和 "leave" 并行循环的 "ownership" 运行。

When one of these threads has completed a loop, and then starts a new loop, it does not have its stack cleaned - all ThreadStatic variables that were set in the first iteration remain for the second iteration

是的。它与 "stack" 没有任何关系。 ThreadStatic是线程专有存储space,所以还是和线程有关

when are these properties removed from memory?

ThreadStatic 是一个线程专有存储space,所以当线程完成时它们会被清理。

Are they cleaned up when the thread is returned to the ThreadPool (presumably when the Parallel.ForEach completes)?

没有。线程仍然存在,所以 ThreadStatic 属性 对象也仍然存在。

I don't particularly like the idea of explicitly setting them to null at the end of each loop iteration

Peter Duniho 说得对:"I don't see anything in the little bit of code you posted that justifies the use of any of [ThreadStatic], ThreadLocal<T> or AsyncLocal<T>. For transient threads, typically the right approach is to just pass an appropriate context object to the thread... All of these other mechanisms are more heavy-weight and have semantics that don't seem to be needed here."