[英]Python Producer/consumer with many threads
I wrote this small application to solve the classical Producer/Consumer problem in Python. 我写了这个小应用程序来解决Python中的经典生产者/消费者问题。 I know that I could use a thread safe Queue mechanism to solve this problem, but I was interested in solving this problem on my own for learning. 我知道我可以使用线程安全的队列机制来解决这个问题,但我有兴趣自己解决这个问题来学习。
from threading import Thread, Condition
from collections import deque
import random
import time
tasks = deque()
condition = Condition()
class Consumer(Thread):
def process_task(self, task):
self.log("Completed task " + str(task) )
def get_task(self):
# this is the only method that access the global object
global tasks
condition.acquire()
if len(tasks) is 0:
self.log("Nothing in the Queue... Sleeping!")
condition.wait()
return None
task = tasks.popleft()
condition.release();
return task
def sleep(self):
self.log("Sleeping")
time.sleep(1)
def execute(self):
while True:
task = self.get_task()
if task:
self.process_task(task)
def run(self):
self.log("Started")
self.execute()
def log(self, msg):
print("[ Consumer: {0} ] Consumed {1}".format(self.getName(), msg))
class Producer(Thread):
def create_tasks(self):
return [random.randint(0,100) for x in range(100)]
def add_tasks(self, new_tasks):
global tasks
condition.acquire()
tasks.extend(new_tasks)
self.log("Produced " + str(len(new_tasks)) + "elements")
self.log("Queue length : " + str(len(tasks)))
self.notify_all()
condition.release();
def notify_all(self):
condition.notify_all()
self.log("Aweking consumers")
def sleep(self):
self.log("Sleeping")
time.sleep(1)
def execute(self):
while True:
new_tasks = self.create_tasks()
self.add_tasks(new_tasks)
self.sleep()
def run(self):
self.log("Started")
self.execute()
def log(self, msg):
print("[ Producer: {0} ] {1}".format(self.getName(), msg))
Producer().start()
Producer().start()
Consumer().start()
Consumer().start()
Consumer().start()
The application appears to work properly. 该应用程序似乎正常工作。 What I mean by this is that I haven't experienced any deadlock or weird exceptions. 我的意思是我没有经历任何僵局或奇怪的例外。 However, the result I've obtained is not what I was expecting as the work is not spread among the different consumers at all. 然而,我获得的结果并不是我所期望的,因为工作根本没有在不同的消费者中传播。 I was expecting that all three consumer thread would have done some work in parallel, but this didn't happen. 我期待所有三个消费者线程都能并行完成一些工作,但这并没有发生。 This is an extract from the logs : 这是日志的摘录:
[ Consumer: Thread-4 ] Consumed Completed task 42
... 98 times the above line with a different task ( always thread 4) ...
[ Consumer: Thread-4 ] Consumed Completed task 22
[ Consumer: Thread-4 ] Consumed Nothing in the Queue... Sleeping!
[ Consumer: Thread-5 ] Consumed Nothing in the Queue... Sleeping!
[ Consumer: Thread-3 ] Consumed Nothing in the Queue... Sleeping!
[ Producer: Thread-1 ] Produced 100elements
[ Producer: Thread-1 ] Queue length : 100
[ Producer: Thread-1 ] Aweking consumers
[ Producer: Thread-1 ] Sleeping
[ Consumer: Thread-3 ] Consumed Completed task 87
... 98 times the above line with a different task ( always thread 3)...
[ Consumer: Thread-3 ] Consumed Completed task 20
[ Consumer: Thread-3 ] Consumed Nothing in the Queue... Sleeping!
[ Consumer: Thread-4 ] Consumed Nothing in the Queue... Sleeping!
[ Consumer: Thread-5 ] Consumed Nothing in the Queue... Sleeping!
As you can see from the above logs, all work is carried out by one single thread whereas the other 2 thread don't do anything. 从上面的日志中可以看出,所有工作都由一个单独的线程执行,而另外两个线程不执行任何操作。 It looks like they never had the chance to run. 看起来他们从来没有机会跑。 I tried to put asleep a consumer thread after it has executed a task, but the behaviour didn't change. 我尝试在执行任务后将消费者线程放入睡眠状态,但行为没有改变。
Is there a bug in my code that I don't see? 我的代码中是否有一个我看不到的错误? Could be this behaviour related to the famous "GIL"? 这种行为可能与着名的“GIL”有关吗?
Thank you for your help. 谢谢您的帮助。
What you're seeing isn't really a coding problem, its just that the same thread is usually winning the race to re-acquire the condition mutex. 你所看到的并不是一个真正的编码问题,它只是同一个线程通常赢得重新获得条件互斥的竞争。 There's enough time for the thread to run process_task
and still win the race, because process_task
is hardly doing anything. 线程有足够的时间来运行process_task
并仍然赢得比赛,因为process_task
几乎没有做任何事情。 I don't think this is actually related to the GIL; 我不认为这实际上与GIL有关; I ran the code in Jython, which has no GIL, and the same behavior occurred. 我在Jython中运行了代码,它没有GIL,并且发生了相同的行为。
There's definitely no bug, though. 但肯定没有错误。 When I ran your code in CPython, I occasionally had more than one thread end up consuming: 当我在CPython中运行你的代码时,我偶尔会遇到多个线程:
[ Producer: Thread-1 ] Started
[ Producer: Thread-1 ] Produced 100elements
[ Producer: Thread-1 ] Queue length : 100
[ Producer: Thread-1 ] Aweking consumers
[ Producer: Thread-1 ] Sleeping
[ Producer: Thread-2 ] Started
[ Consumer: Thread-3 ] Consumed Started
[ Producer: Thread-2 ] Produced 100elements
[ Producer: Thread-2 ] Queue length : 200
[ Consumer: Thread-5 ] Consumed Started
[ Producer: Thread-2 ] Aweking consumers
[ Consumer: Thread-4 ] Consumed Started
[ Producer: Thread-2 ] Sleeping
[ Consumer: Thread-5 ] Consumed Completed task 53
[ Consumer: Thread-4 ] Consumed Completed task 73
[ Consumer: Thread-5 ] Consumed Completed task 83
[ Consumer: Thread-4 ] Consumed Completed task 71
[ Consumer: Thread-5 ] Consumed Completed task 67
[ Consumer: Thread-4 ] Consumed Completed task 7
[ Consumer: Thread-5 ] Consumed Completed task 34
[ Consumer: Thread-4 ] Consumed Completed task 68
[ Consumer: Thread-5 ] Consumed Completed task 15
[ Consumer: Thread-4 ] Consumed Completed task 29
[ Consumer: Thread-5 ] Consumed Completed task 20
... (4 and 5 continue to switch off)
Also, I get very normal results if I call self.sleep()
inside of process_task
, which simulates some real work actually happening, and therefore makes for a much more realistic test: 另外,如果我在process_task
调用self.sleep()
来模拟一些实际发生的实际工作,那么我会得到非常正常的结果,因此可以进行更加真实的测试:
[ Producer: Thread-1 ] Started
[ Producer: Thread-2 ] Started
[ Producer: Thread-1 ] Produced 100elements
[ Producer: Thread-1 ] Queue length : 100
[ Producer: Thread-1 ] Aweking consumers
[ Consumer: Thread-5 ] Consumed Started
[ Consumer: Thread-3 ] Consumed Started
[ Producer: Thread-1 ] Sleeping
[ Producer: Thread-2 ] Produced 100elements
[ Consumer: Thread-4 ] Consumed Started
[ Producer: Thread-2 ] Queue length : 200
[ Producer: Thread-2 ] Aweking consumers
[ Producer: Thread-2 ] Sleeping
[ Consumer: Thread-3 ] Consumed Sleeping
[ Consumer: Thread-5 ] Consumed Sleeping
[ Consumer: Thread-4 ] Consumed Sleeping
[ Producer: Thread-1 ] Produced 100elements
[ Consumer: Thread-3 ] Consumed Completed task 85
[ Consumer: Thread-5 ] Consumed Completed task 31
[ Producer: Thread-1 ] Queue length : 297
[ Consumer: Thread-4 ] Consumed Completed task 62
[ Producer: Thread-1 ] Aweking consumers
[ Producer: Thread-1 ] Sleeping
[ Producer: Thread-2 ] Produced 100elements
[ Producer: Thread-2 ] Queue length : 397
[ Producer: Thread-2 ] Aweking consumers
[ Producer: Thread-2 ] Sleeping
[ Consumer: Thread-3 ] Consumed Sleeping
[ Consumer: Thread-5 ] Consumed Sleeping
[ Consumer: Thread-4 ] Consumed Sleeping
[ Producer: Thread-1 ] Produced 100elements
[ Producer: Thread-1 ] Queue length : 494
[ Consumer: Thread-3 ] Consumed Completed task 99
[ Consumer: Thread-4 ] Consumed Completed task 58
[ Producer: Thread-1 ] Aweking consumers
[ Consumer: Thread-5 ] Consumed Completed task 18
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.