2009-04-13 3 views
4

Я очищаю код Python, который я написал, когда был ... не так хорошо осведомлен. В первую очередь я убиваю некоторые сложности, связанные с неполным пониманием потоков в Python. Мне нужно сделать список элементов потокобезопасными, и я хотел бы сделать это через неизменяемые списки, а не обычный подход к блокировке. Я знаю, что неизменяемые объекты очень специфичны в отношении потоковой обработки, потому что все проблемы безопасности потоков, связанные с неполными изменениями состояния, просто исчезают.Неизменность и безопасность потоков в Python

Итак, я спрашиваю: есть ли следующий код в потоковом режиме?

class ImmutableList(object): 
    def __init__(self): 
     self._list =() 

    def __iter__(self): 
     return self._list.__iter__() 

    def append(self, x): 
     self._list = self._list + tuple([x]) 

Я думаю, что это так, потому что новый список строится каждый раз. Если список обновляется, а другой поток выполняет итерацию через него, старый список будет продолжать использоваться для оставшейся части итерации. Это хорошо для меня, но может быть не для всех.

Кроме того, это хорошая идея? Я только хочу применить это к нескольким ситуациям, когда размер списка невелик, и списки не сильно меняются (ожидают прослушиватели событий).

+4

Кстати, вы можете написать «кортеж ([х])», как «(х,)». –

ответ

15

Прежде всего, добавление к списку уже является потокобезопасным в ссылочной реализации CPython языка программирования Python. Другими словами, хотя спецификация языка не требует, чтобы класс списка был потокобезопасным, это в любом случае. Поэтому, если вы не используете Jython или IronPython или какую-то другую реализацию Python, тогда вы в порядке.

Во-вторых, вам также необходимо перегрузить другие операции с списком, такие как __setitem__ и __setslice__ и т. Д. Я предполагаю, что ваша реализация обрабатывает это.

Наконец, ответ на ваш вопрос: нет, ваш код не является потокобезопасным. Рассмотрим следующую ситуацию:

  • Ваш список содержит (5, 6)
  • Thread 1 пытается добавить 7, а поток 2 пытается добавить 8
  • резьбы 1 конструктов другой кортеж (5, 6, 7) и до этого может быть назначен _list, есть переключатель контекста
  • Тема 2 выполняет свое назначение, поэтому список теперь (5, 6, 8)
  • Тема 1 получает контроль над процессором назад и назначает _list , перезаписывая предыдущее добавление. Список теперь (5, 6, 7), а 8 потерян.

Мораль этой истории заключается в том, что вы должны использовать блокировку и избегать умения.

+0

Никогда не думал об этом случае ... спасибо! –

+1

+1: Просто используйте замки. –

+2

+1 для конкретного контрпримера – wkschwartz

4

Настоящая неизменная реализация списка не позволит изменить структуру основного списка, например, вы здесь. Как отметил @ [Eli Courtwright], ваша реализация не является потокобезопасной. Это потому, что он не является действительно неизменным. Чтобы сделать неизменную реализацию, любые методы, которые изменили бы список, вместо этого вернут новый список, отражающий желаемое изменение.

Что касается вашего примера кода, для этого потребуется вам, чтобы сделать что-то вроде этого:

class ImmutableList(object): 
    def __init__(self): 
    self._list =() 

    def __iter__(self): 
    return self._list.__iter__() 

    def append(self, x): 
    return self._list + tuple([x]) 
+1

+1 для вашего примера того, как действительно работают структуры данных.Однако я должен указать, что ваш пример будет бесполезен для Мэтта Грина, которому нужна структура данных, которая может быть одновременно изменена несколькими потоками. –

+0

Я не думаю, что это то, что ему действительно нужно, на самом деле трудно сказать, что ему нужно от описания, но поскольку он говорит о неизменяемых структурах, кажется, что он либо не хочет одновременно изменять его, или он действительно не понимает правильное использование для неизменяемых –

Смежные вопросы