Правильне використання мьютексів у Python


77

Я починаю з декількох потоків у python (або принаймні можливо, що мій сценарій створює кілька потоків). чи був би цей алгоритм правильним використанням Mutex? Я ще не тестував цей код, і він, мабуть, навіть не буде працювати. Я просто хочу, щоб processData запускався в потоці (по одному), а основний цикл while продовжував працювати, навіть якщо в черзі є потік.

from threading import Thread
from win32event import CreateMutex
mutex = CreateMutex(None, False, "My Crazy Mutex")
while(1)
    t = Thread(target=self.processData, args=(some_data,))
    t.start()
    mutex.lock()

def processData(self, data)
    while(1)
        if mutex.test() == False:
            do some stuff
            break

Редагувати: перечитавши мій код, я бачу, що це вкрай неправильно. але привіт, ось чому я тут прошу допомоги.


Дуже важко зрозуміти, що ви намагаєтесь зробити. Вам потрібно буде детальніше пояснити свій намір.
Марсело Кантос

@Marcelo Cantos, вибачте, що ви, мабуть, праві. Я хочу, щоб мій код у processData починався з нового кроку. Я хочу лише один потік, щоб мати можливість обробляти дані за раз і в порядку, в якому дані були надіслані для обробки даних. Я також хочу, щоб основний цикл while продовжував цикл, поки інші потоки знаходяться в черзі.
Річард

@Richard: Чому ви хочете використовувати потоки, якщо ви все одно плануєте серіалізувати всю обробку? Що поганого в простому циклі? Крім того, чому ви хочете, щоб основна нитка продовжувала цикл? Він просто спалить процесор, можливо, голодуючи інші потоки.
Marcelo Cantos

@Marcelo Cantos, Це не моя реальна програма. Мій фактичний сценарій налічує понад 500 рядків коду, що включає записи бази даних та електронну пошту. Я хочу, щоб основний потік просто прочитав послідовний порт, щоб було менше шансів заповнити буфер до обробки отриманих даних. Виправте мене, якщо я помиляюся, але я думаю, що саме тут можна було б використовувати або потоки, або багатопроцесорну обробку
Річард

@Richard: Це має сенс для основного потоку, але якщо вся інша обробка буде послідовною, то ви можете просто створити ще один потік, щоб виконувати всю цю іншу роботу.
Марсело Кантос,

Відповіді:


164

Я не знаю, чому ви використовуєте вікно Mutex замість Python. За допомогою методів Python це досить просто:

from threading import Thread, Lock

mutex = Lock()

def processData(data):
    mutex.acquire()
    try:
        print('Do some stuff')
    finally:
        mutex.release()

while True:
    t = Thread(target = processData, args = (some_data,))
    t.start()

Але зауважте, через архітектуру CPython (а саме Global Interpreter Lock ) у вас у будь-якому випадку одночасно буде працювати лише один потік - це чудово, якщо декілька з них пов’язані з операціями вводу-виводу, хоча вам захочеться максимально звільнити замок, щоб потік вводу / виводу не блокував інші потоки.

Альтернативою для Python 2.6 та пізніших версій є використання multiprocessingпакету Python . Він відображає threadingпакет, але створить абсолютно нові процеси, які можуть працювати одночасно. Тривіально оновити приклад:

from multiprocessing import Process, Lock

mutex = Lock()

def processData(data):
    with mutex:
        print('Do some stuff')

if __name__ == '__main__':
    while True:
        p = Process(target = processData, args = (some_data,))
        p.start()

Я спробував ваш код, і я отримую цю помилку: з mutex: SyntaxError: неправильний синтаксис. Я думаю, я міг би використати спробу: за винятком: у своїй функції я використовую python 2.4
Річард

6
withє Python 2.5 і multiprocessingє Python 2.6. Відповідно відредаговано.
Chris B.

1
"це нормально, якщо деякі з них пов'язані з введенням / виводом" Насправді це також добре, якщо вони пов'язані з процесором, якщо це відбувається у викликах бібліотек, написаних на C, а не на чистому коді Python (наприклад, якщо ви маніпулювання великими матрицями в numpy), оскільки GIL розблоковується під час цих дзвінків.
Артур Такка

1
@demented hedgehog: mutexмодуль застарілий. Створення мьютексів за допомогою модулів threadingі multiprocessing, що робиться у відповіді, не застаріло.
tjalling

чудово для вводу-виводу, спеціально вирівнювання повідомлень
eusoubrasileiro

14

Це рішення, яке я придумав:

import time
from threading import Thread
from threading import Lock

def myfunc(i, mutex):
    mutex.acquire(1)
    time.sleep(1)
    print "Thread: %d" %i
    mutex.release()


mutex = Lock()
for i in range(0,10):
    t = Thread(target=myfunc, args=(i,mutex))
    t.start()
    print "main loop %d" %i

Вихід:

main loop 0
main loop 1
main loop 2
main loop 3
main loop 4
main loop 5
main loop 6
main loop 7
main loop 8
main loop 9
Thread: 0
Thread: 1
Thread: 2
Thread: 3
Thread: 4
Thread: 5
Thread: 6
Thread: 7
Thread: 8
Thread: 9

17
Існує потенційний тупик. Якщо оператор print видає виняток, ви ніколи не звільняєте мьютекс. Вам потрібно скористатися try/releaseабо withпереконатися, що замок звільнений. Дивіться мою відповідь.
Chris B.

5
Крім того, немає необхідності передавати мьютекс як аргумент функції. Він доступний у глобальному масштабі.
Chris B.


8

Я хотів би ще трохи покращити відповідь від chris-b .

Дивіться нижче мій код:

from threading import Thread, Lock
import threading
mutex = Lock()


def processData(data, thread_safe):
    if thread_safe:
        mutex.acquire()
    try:
        thread_id = threading.get_ident()
        print('\nProcessing data:', data, "ThreadId:", thread_id)
    finally:
        if thread_safe:
            mutex.release()


counter = 0
max_run = 100
thread_safe = False
while True:
    some_data = counter        
    t = Thread(target=processData, args=(some_data, thread_safe))
    t.start()
    counter = counter + 1
    if counter >= max_run:
        break

У вашому першому запуску, якщо ви встановите thread_safe = Falseцикл while, мьютекс не використовуватиметься, і потоки будуть переходити один одного в способі друку, як показано нижче;

Not Thread safe

але, якщо ви встановите thread_safe = Trueі запустите його, ви побачите, що всі результати виходять абсолютно нормальними;

Нитка безпечна

сподіваюся, це допоможе.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.