Как атомарно увеличить статический член в IronPython? - PullRequest
3 голосов
/ 13 февраля 2010

У меня есть сценарий IronPython , который использует TPL и Parallel.ForEach для обработки файлов с использованием нескольких потоков. В C # я могу использовать Interlocked.Add и Interlocked.Increment для изменения глобальных переменных в атомарной поточно-ориентированной операции, но это не работает в IronPython, потому что целые числа неизменны . В настоящее время у меня есть простой класс Results, который хранит несколько переменных в виде статических членов, которые используются для отслеживания результатов многопоточной операции. При изменении нескольких значений я могу заблокировать класс с помощью класса .NET Monitor , чтобы гарантировать, что обновление является поточно-ориентированным, но это выглядит слишком затратно, если я хочу обновить только одну переменную (например, просто увеличить Results.Files).

У меня вопрос: есть ли лучший способ увеличить одну статическую переменную-член, такую ​​как Results.Files в IronPython, потокобезопасным или атомарным способом, аналогичным тому, как работает Interlocked.Increment? В качестве альтернативы есть какие-нибудь поточно-безопасные счетчики, встроенные в python или .NET Framework, которые можно использовать вместо базового целого числа?

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 ]

2 голосов
/ 23 февраля 2010

Похоже, что 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 НЕ будут генерировать исключение переполнения (оно просто молча переносится), поэтому убедитесь, что вы выбрали достаточно большой тип данных, чтобы никогда не переполняться.

0 голосов
/ 23 февраля 2010

Если вы хотите использовать бит C #, вы можете создать простой повторно используемый класс C #, который инкапсулирует (нестатическую) переменную-член int и предоставляет функции Interlocked.

class InterlockedWrapper
{
     private int _value;

     public int Increment()
     {
          return Interlocked.Increment(ref _value);
     }
....

и так далее. Затем вы можете использовать этот класс из Python.

0 голосов
/ 16 февраля 2010

В прошлом я разделял работу для параллельной обработки, сохранял результаты с единицей работы и в конце сопоставлял их. Думайте Карта / Уменьшите, и у вас есть это.

Создайте новый поток, который поглощает ваши кортежи по мере их поступления (или подождите, пока все не будет сделано). Этот метод инкрементного или полного суммирования должен вызываться в конце или быть единственным, который считывает из очереди и увеличивает счетчики.

Измените метод add, чтобы поместить результаты в виде кортежа в очередь.

Надеюсь, это поможет.

Jacob

...