Jef*_*nal 17 .net java oop multithreading thread-local
这个问题让我想到了Java和.NET等高级开发框架中的线程局部存储.
Java有一个ThreadLocal<T>类(也许还有其他构造),而.NET有数据槽,很快就有ThreadLocal<T>了它自己的类.(它也有ThreadStaticAttribute,但我对成员数据的线程局部存储特别感兴趣.)大多数其他现代开发环境为语言或框架级别提供了一种或多种机制.
线程局部存储解决了什么问题,或者线程局部存储提供了什么优势,而不是创建单独的对象实例以包含线程本地数据的标准面向对象的习惯用法?换句话说,这是怎么回事:
// Thread local storage approach - start 200 threads using the same object
// Each thread creates a copy of any thread-local data
ThreadLocalInstance instance = new ThreadLocalInstance();
for(int i=0; i < 200; i++) {
ThreadStart threadStart = new ThreadStart(instance.DoSomething);
new Thread(threadStart).Start();
}
Run Code Online (Sandbox Code Playgroud)
优于此?
// Normal oo approach, create 200 objects, start a new thread on each
for(int i=0; i < 200; i++) {
StandardInstance standardInstance = new StandardInstance();
ThreadStart threadStart = new ThreadStart(standardInstance.DoSomething);
new Thread(threadStart).Start();
}
Run Code Online (Sandbox Code Playgroud)
我可以看到,使用具有线程本地存储的单个对象可以稍微提高内存效率,并且由于分配(和构造)较少而需要较少的处理器资源.还有其他优点吗?
Ree*_*sey 11
线程局部存储解决了什么问题,或者线程局部存储提供了什么优势,而不是创建单独的对象实例以包含线程本地数据的标准面向对象的习惯用法?
线程本地存储允许您为每个正在运行的线程提供一个类的唯一实例,这在尝试使用非线程安全类时非常有用,或者在尝试避免由于共享状态而可能发生的同步要求时非常有用.
至于优势与您的示例 - 如果您生成单个线程,使用线程本地存储而不是传入实例几乎没有优势. ThreadLocal<T>然而,当使用ThreadPool(直接或间接)工作时,类似的结构变得非常有价值.
例如,我最近有一个特定的过程,我们正在使用.NET中的新任务并行库进行一些非常繁重的计算.执行的计算的某些部分可以被缓存,并且如果缓存包含特定匹配,我们可以在处理一个元素时节省相当多的时间.但是,缓存的信息具有较高的内存要求,因此我们不希望缓存超过上一个处理步骤.
但是,尝试跨线程共享此缓存是有问题的.为了做到这一点,我们必须同步对它的访问,并在我们的类中添加一些额外的检查,以使它们线程安全.
我没有这样做,而是重写了算法,允许每个线程在一个中维护自己的私有缓存ThreadLocal<T>.这允许每个线程维护自己的私有缓存.由于TPL使用的分区方案倾向于将元素块保持在一起,因此每个线程的本地缓存倾向于包含它所需的适当值.
这消除了同步问题,但也使我们能够保持缓存.在这种情况下,整体效益非常大.
有关更具体的示例,请查看我在使用TPL汇总时编写的博文.在内部,Parallel类使用ThreadLocal<TLocal>每当你使用ForEach重载时保持本地状态(以及Parallel.For<TLocal>方法).这是每个线程保持本地状态分开以避免锁定的方式.
偶尔,有线程本地状态是有帮助的.一个示例是日志上下文 - 设置您当前正在服务的请求的上下文或类似的内容可能很有用,这样您就可以整理所有日志以处理该请求.
另一个很好的例子是System.Random.NET.众所周知,每次要使用时都不应创建新实例Random,因此有些人创建单个实例并将其放在静态变量中......但这很尴尬,因为Random它不是线程安全的.相反,你真的想要每个线程一个实例,适当播种.ThreadLocal<T>对此非常有用.
类似的例子是与线程相关的文化或安全上下文.
一般来说,这是一个不想在整个地方传递太多上下文的情况.你可以让每一个方法调用都包含一个"RandomContext"或一个"LogContext" - 但它会妨碍你的API的清洁 - 如果你不得不调用另一个可以回调给它的API那么链就会被打破你的虚拟方法或类似的东西.
在我看来,线程本地数据应尽可能避免 - 但偶尔它可能真的很有用.
我会说,在大多数情况下,你可以逃避它是静态的 - 但偶尔你可能想要每个实例,每线程信息.再次,值得用你的判断来看看它有用的地方.
| 归档时间: |
|
| 查看次数: |
8438 次 |
| 最近记录: |