在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:如果计划无论如何都要序列化所有处理,为什么要使用线程?简单循环有什么问题?另外,为什么要让主线程保持循环?它只会烧毁CPU,可能会饿死其他线程。
Marcelo Cantos

@Marcelo Cantos,这不是我的实际程序。我的实际脚本已经达到了500多行代码,其中包括数据库条目和电子邮件。我希望主线程只读取一个串行端口,以便在处理接收到的数据之前填充缓冲区的机会较小。如果我错了,请纠正我,但我认为这正是使用线程或多处理的地方
理查德(Richard)2010年

@Richard:这对于主线程是有意义的,但是如果所有其他处理都将是顺序的,那么您可以创建另一个线程来完成所有其他工作。
马塞洛·坎托斯

Answers:


159

我不知道您为什么要使用Window的Mutex而不是Python的Mutex。使用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),无论如何一次您实际上只能运行一个线程-如果其中许多线程受I / O约束,这很好,尽管您会想要释放尽可能多的锁,以使I / O绑定线程不会阻止其他线程运行。

对于Python 2.6和更高版本,一种替代方法是使用Python的multiprocessing软件包。它镜像了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()

我尝试了您的代码,但出现此错误:互斥锁:SyntaxError:语法无效。我想我可以使用try:例外:在我的函数中,我使用的是python 2.4
Richard Richard 2010年

6
with是Python 2.5和multiprocessingPython 2.6。进行了相应的编辑。
克里斯·B

1
“如果它们中有许多是I / O绑定的,这很好。”​​实际上,如果它们是CPU绑定的,那也很好,只要这是对用C而不是纯Python代码编写的库的调用(例如,如果您使用在numpy中处理大型矩阵),因为在这些调用期间GIL已解锁。
亚瑟·塔卡

1
@demented刺猬:该mutex模块已弃用。不建议使用在答案中完成的使用threadingmultiprocessing模块创建互斥锁。
tjalling

出色的I / O消息对齐方式
eusoubrasileiro

13

这是我想出的解决方案:

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语句引发Exception,则永远不会释放互斥量。您需要使用try/releasewith确保释放了锁定。看我的答案。
克里斯·B

5
同样,也不需要将互斥锁作为函数的参数传递。它在全球范围内都可用。
克里斯·B


8

我想改善的答案克里斯-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循环,则互斥量将不被使用,并且线程将在打印方法中互为过渡,如下所示;

线程不安全

但是,如果您设置thread_safe = True并运行它,您将看到所有输出都很好。

线程安全

希望这可以帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.