how to use exactly Sleep and wakeup in python3?(multithread)

Asked

Viewed 686 times

1

Hello I am trying to do in python3 the famous problem of the producer/consumer, transcribing from my book of S.O. however I have been facing a problem , apparently the Wait() Just put it to sleep once in the loop or something, because just after the first time the threads run loose. code:

# -*- coding: utf-8 -*-
import threading
acordar_dormir_produtor = threading.Event()
acordar_dormir_consumidor = threading.Event()
bufer = 0
bufer_max = 10


def consumidor():
    global acordar_dormir_produtor
    global acordar_dormir_consumidor
    global bufer
    global bufer_max
    while True:
        if bufer == 0:
            print ("consumidor dormindo")
            acordar_dormir_consumidor.wait()
            print ("consumidor acordou")
        bufer = bufer - 1
        if bufer == (bufer_max - 1):
            print ("distracando produtor")
            acordar_dormir_produtor.set()
            acordar_dormir_produtor.clear()
        print ("consumidor",bufer)


def produtor():
    global acordar_dormir_produtor
    global acordar_dormir_consumidor
    global bufer
    global bufer_max
    while True:
        if bufer == bufer_max:
            print ("produtor dormindo")
            acordar_dormir_produtor.wait()
            print ("produtor acordou")
        bufer = bufer + 1
        if bufer == 1:
            print ("distracando consumidor")
            acordar_dormir_consumidor.set()
            acordar_dormir_produtor.clear()
        print ("produtor ",bufer)
print (bufer)
b = threading.Thread(target=produtor)
a = threading.Thread(target=consumidor)
a.start()
b.start()
#while True:
   # time.sleep(3)
   # print (bufer)

how to do this? has another object since the Event doesn’t work? I tried the conditions unsuccessfully(actually didn’t even get to perform)

2 answers

1

Using a Queue as drgarcia suggests is a higher-level solution to a real problem.

Your situation however is didactic - and this is what I think is going wrong: you call the set and the clear of Event next to each other. The Python threading implementation is not something that uses event signature - it is done by Polling - that is, from time to time the execution will pass to the thread that is waiting in a event.wait and see if their status has changed - but this check is not triggered automatically by the call to Event.set - then you would have to give a lot of (but a lot of) luck for the thread to be called exactly between your chaamda a set and the call to clear next.

For what you made it work, put the call to clear immediately before of the call to wait in the thread that will make Wait (and not in the thread that will send the set):

...
def consumidor():
   ...
   acordar_dormir_consumidor.clear()      
   acordar_dormir_consumidor.wait()

...
def produtor():
    ...
    acordar_dormir_consumidor.set()

0

You can use the class Thread and the class Queue (which is thread-safe). To sleep you can use the time.sleep.

# -*- coding: utf-8 -*-
from threading import Thread
import time
import random
from queue import Queue


queue = Queue(10)


class ProducerThread(Thread):
    def run(self):
        nums = range(5)
        global queue
        while True:
            num = random.choice(nums)
            queue.put(num)
            print("Produced", num)
            time.sleep(random.random())


class ConsumerThread(Thread):
    def run(self):
        global queue
        while True:
            num = queue.get()
            queue.task_done()
            print("Consumed", num)
            time.sleep(random.random())


ProducerThread().start()
ConsumerThread().start()

The Producer feeds the buffer in the method Queue.put() while the Consumer reads the buffer through the method Queue.get() (and signal that the buffer reading was successful through the method Queue.task_done()). After each one does his task, he goes to "sleep" for a random time.

The method Queue.get() wait for a value in the queue, so regardless of who starts working first, the consumer will only proceed when there is something in the queue to be a consumer.

If the example needs to be more didactic, you can use the class threading.Condition:

# -*- coding: utf-8 -*-
from threading import Thread, Condition
import time
import random


queue = []
MAX_NUM = 10
condition = Condition()


class ProducerThread(Thread):
    def run(self):
        nums = range(5)
        global queue
        while True:
            condition.acquire()
            if len(queue) == MAX_NUM:
                print("Queue full, producer is waiting")
                condition.wait()
                print("Space in queue, Consumer notified the producer")
            num = random.choice(nums)
            queue.append(num)
            print("Produced", num)
            condition.notify()
            condition.release()
            time.sleep(random.random())


class ConsumerThread(Thread):
    def run(self):
        global queue
        while True:
            condition.acquire()
            if not queue:
                print("Nothing in queue, consumer is waiting")
                condition.wait()
                print("Producer added something to queue and notified the consumer")
            num = queue.pop(0)
            print("Consumed", num)
            condition.notify()
            condition.release()
            time.sleep(random.random())


ProducerThread().start()
ConsumerThread().start()

With the condition you can do "lock" to control when consumer and producer should wait and when they should work.

Being objective in answering your question, the condition.wait() makes the thread sleep waiting for the condition.notify() which acts as the "wakeup".

Reference: http://agiliq.com/blog/2013/10/producer-consumer-problem-in-python/

Browser other questions tagged

You are not signed in. Login or sign up in order to post.