LINUX.ORG.RU

Мой тупняк или вопиющий баг в Python multiprocessing?

 , ,


2

3

Изучая способы распараллелить код в Python столкнулся с разным поведением Shared memory модуля multiprocessing на Linux и Windows.

Вот код, демонстрирующий такое неопределенное поведение:

from time import sleep
from multiprocessing import Process, Value

c = Value('d', 5)

def simpleTimer():
    global c
    while c.value > 0:
        sleep(1)
        c.value -= 1
        print('Value in second process:', c.value)

if __name__ == '__main__':
    p = Process(target=simpleTimer)
    p.start()

    while c.value > 0:
        sleep(1)
        print('Value in main process:', c.value)

Я специально собрал Python 3.8.2 из тарболла, чтобы не пенять на патчи дистрибутива. Вот соответствующий моим ожиданиям результат на Ubuntu 19.10:

$ ./python test_mp.py 
Value in main process: 5.0
Value in second process: 4.0
Value in main process: 4.0
Value in second process: 3.0
Value in main process: 3.0
Value in second process: 2.0
Value in main process: 2.0
Value in second process: 1.0
Value in main process: 1.0
Value in second process: 0.0
Value in main process: 0.0
$ 

А вот поведение той же версии Python под Windows 10 LTSC:

>python D:\test_mp.py
Value in main process: 5.0
Value in second process: 4.0
Value in main process: 5.0
Value in second process: 3.0
Value in main process: 5.0
Value in second process: 2.0
Value in main process: 5.0
Value in second process: 1.0
Value in main process: 5.0
Value in second process: 0.0
Value in main process: 5.0
Value in main process: 5.0
Traceback (most recent call last):
  File "D:\test_mp.py", line 18, in <module>
    sleep(1)
KeyboardInterrupt
^C
>

Скрипт сам не завершается.

Из документации CPython:

the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.

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

★★

Последнее исправление: ArkaDOSik (всего исправлений: 1)

Скрипт сам не завершается.

Для вендовой консоли это норма. Применяй колдунство и костыли.

anonymous
()
if __name__ == '__main__':

Ну и уродство. Я думал, что только си/++-прогеры с радостью согласны жрать подобное дерьмо. Оказалось и питонщики тоже. Какая печаль.

anonymous
()
Ответ на: комментарий от WitcherGeralt

В питоне разница между переменной и ссылкой на нее не всегда очевидна, поэтому я сделал так (если бы это был обычный вызов функции, то переменная так же изменялась в глобальной видимости, а вот с передачей аргумента создалась бы копия переменной). Во всяком случае винда в виртуалке начала установку 100500 обновлений, как всегда. Пока там протестировать не могу.

ArkaDOSik ★★
() автор топика
Последнее исправление: ArkaDOSik (всего исправлений: 1)

Потому, что в Windows нет fork с Copy-On-Write. Твой global c просто не работает на ней. Передавай через параметры Process.

xpahos ★★★★★
()
Ответ на: комментарий от xpahos

Да, с передачей аргумента работает везде, проверил.

threading получается более высокоуровневый, но потоки так просто не убить.

ArkaDOSik ★★
() автор топика
Ответ на: комментарий от ArkaDOSik

В питоне разница между переменной и ссылкой на нее не всегда очевидна

Дело не в переменных и ссылках, переменная — всего лишь имя, никакой магии тут нет. Просто в питоне есть изменяемые и неизменяемые типы.

а вот с передачей аргумента создалась бы копия переменной

На самом деле нет. Смотри:

In [1]: def f(A, B, C, D): 
   ...:     print(id(A), id(B), id(C), id(D)) 
   ...:     A += 1 
   ...:     print(id(A), id('bar')) 
   ...:

In [2]: a, b, c, d = 1, 2, 1000, 'bar'

In [3]: print(id(a), id(b), id(c), id(d))
9302176 9302208 139775249962960 139775279152880

In [4]: f(a, b, c, d)
9302176 9302208 139775249962960 139775279152880
9302208 139775279152880

Здесь ты можешь видеть как в функцию передаются ровно те объекты, которые ты передал, а никакие не копии. Также ты тут можешь наблюдать кеши.

А __iadd__ (+=) — это вообще фейк.

WitcherGeralt ★★
()
Последнее исправление: WitcherGeralt (всего исправлений: 1)
Ответ на: комментарий от anonymous

Если уж так нужна многопоточность на питоне, лучше уж интерпретаторы запускать через subprocess (пока subinterpreters недозавезли), волосы внизу спины целее будут.

PolarFox ★★★★★
()
Ответ на: комментарий от WitcherGeralt

int, float, str, tuple действительно неизменяемые. Похоже, что += и -= меня запутали :)

ArkaDOSik ★★
() автор топика

Я специально собрал Python 3.8.2 из тарболла, чтобы не пенять на патчи дистрибутива. Вот соответствующий моим ожиданиям результат на Ubuntu 19.10
...
А вот поведение той же версии Python под Windows 10 LTSC

Здравствуйте, я sharedctypes, и я — кусок дерьма. Да, это абсолютно нормальное поведение для меня. Я работаю на форкнутых данных, потому ваша винда без форка мне не подходит.

этот баг уже бы давно отловили будь он таковым.

sharedctypes только в 3.7 появился, так что ни о каком «давно» речи быть не может.

byko3y ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.