我有一个IronPython script,它使用TPL和Parallel.ForEach来处理使用多个线程的文件。在C#中,我可以使用Interlocked.Add和Interlocked.Increment在原子线程安全操作中更改全局变量,但这在IronPython中不起作用,因为integers are immutable。我目前有一个简单的结果类,它将一些变量存储为静态成员,用于跟踪多线程操作的结果。当更改多个值时,我可以使用.NET Monitor类来锁定类,以确保更新是线程安全的,但是如果我只想更新单个变量(比如只是增加结果),这似乎是很多开销。文件)。
我的问题是,是否有更好的方法以类似于Interlocked.Increment工作方式的线程安全或原子方式在IronPython中增加像Results.Files这样的静态成员变量?或者可以使用内置于python或.NET框架中的任何线程安全计数器来代替基本整数吗?
class Results:
Files = 0
Lines = 0
Tolkens = 0
@staticmethod
def Add(intFiles, intLines, intTolkens):
#Use the System.Threading.Monitor class to ensure addition is thread safe
Monitor.Enter(Results)
Results.Files += intFiles
Results.Lines += intLines
Results.Tolkens += intTolkens
Monitor.Exit(Results) #Finish thread safe code
答案 0 :(得分:2)
看起来python这样做的方法就是使用multiprocessing.Value对象,默认情况下会在访问对象时锁定对象。遗憾的是,多处理类没有内置到IronPython中,因为它基于CTypes。然而,我确实找到了使用Interlocked类和对CLR对象的引用的方法:
import clr
from System.Threading import Interlocked
refInt = clr.Reference<int>(5) #Create a reference to an integer
#refInt = <System.Int32 object at 0x0000000000000049 [5]>
#refInt.Value = 5
Interlocked.Increment(refInt) #Returns 6 and refInt now points to a new integer
#refInt = <System.Int32 object at 0x000000000000004A [6]>
#refInt.Value = 6
在这种情况下,您可以使用所有Interlocked方法来添加,比较,交换,增量和读取refInt对象。您也可以直接获取或设置refInt.Value,但只有Interlocked方法是线程安全的。 Interlocked方法will NOT throw an overflow exception(它将只是静默包装),所以请确保选择一个足够大的数据类型,永远不会溢出。
答案 1 :(得分:0)
过去,我将工作分为并行处理,将结果存储在工作单元中,最后整理它们。想想Map / Reduce,你就拥有它。
创建一个新的线程,当它们进来时吞噬你的元组(或等到一切都完成)。应该在结束时调用此增量或完整求和方法,或者是从队列中读取并递增计数器的唯一内容。
更改add方法,将结果作为元组放入队列中。
希望这有帮助。
雅各
答案 2 :(得分:0)
如果您愿意使用C#,您可以创建一个简单的可重用C#类,它封装(非静态)int
成员变量并提供Interlocked函数。
class InterlockedWrapper
{
private int _value;
public int Increment()
{
return Interlocked.Increment(ref _value);
}
....
等等。然后,您可以使用Python中的此类。