如何在IronPython中以原子方式增加静态成员?

时间:2010-02-12 22:11:07

标签: .net multithreading ironpython

我有一个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

3 个答案:

答案 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中的此类。