Reputation: 112
I am trying to run two functions(tasks) sequentially using multithreading. The problem is when I am implementing it executes one function completely then steps into the another one. What I expected is to step over to another function while loop completes its first run in the given function.
As a side note, I set time.sleep()
to simulate the receiving and preparation scenario of some order.
from collections import deque
import time
from threading import Thread
class Order_queue:
def __init__(self):
self.buffer = deque()
def place_order(self, basket):
for item in basket:
self.buffer.appendleft(item)
print(f"Ordered: {item}")
time.sleep(3)
def serve_order(self):
time.sleep(1)
while not self.is_empty():
served = self.buffer.pop()
time.sleep(2)
print(f"Served: {served}")
def is_empty(self):
return len(self.buffer)==0
order_list = ['pizza','samosa','pasta','biryani','burger']
order = Order_queue()
th1 = Thread(target=order.place_order, args=(order_list,))
th2 = Thread(target=order.serve_order)
th1.start()
th2.start()
th1.join()
th2.join()
Output:
Ordered: pizza
Ordered: samosa
Ordered: pasta
Ordered: biryani
Ordered: burger
Served: pizza
Served: samosa
Served: pasta
Served: biryani
Served: burger
Expected:
Ordered: pizza
Served: pizza
Ordered: samosa
Served: samosa
....
Upvotes: 0
Views: 91
Reputation: 123453
You don't need threads to run two functions (tasks) sequentially because it can be done in by using coroutines (a form of non-preemptive multitasking), which are relatively simple to create in Python because all it requires is using a yield
expression inside a function or method.
Similarly, you don't need a deque
or some other buffer for the tasks since there's never more than one being processed (i.e. ordered and served) at a time.
Here's how to implement things using the approach I am suggesting. Note that the two functions involved really don't need to be part of a class
, but I put them in one anyway to make things a little more like the code in your question.
import time
def coroutine(func):
""" Decorator to make coroutines automatically start when called. """
def start(*args, **kwargs):
cr = func(*args, **kwargs)
next(cr)
return cr
return start
class OrderQueue:
@coroutine
def serve_orders(self):
while True:
served = (yield)
print(f'Served: {served}')
time.sleep(2)
def place_orders(self, basket):
server = self.serve_orders() # Initialize coroutine generator method.
for item in basket:
print(f'Ordering: {item}')
server.send(item)
time.sleep(0.5)
server.close() # Causes coroutine generator method to exit.
order_list = ['pizza', 'samosa', 'pasta', 'biryani', 'burger']
order_queue = OrderQueue()
order_queue.place_orders(order_list)
print('-- Fini --')
Output:
Ordering: pizza
Served: pizza
Ordering: samosa
Served: samosa
Ordering: pasta
Served: pasta
Ordering: biryani
Served: biryani
Ordering: burger
Served: burger
-- Fini --
Upvotes: 1
Reputation: 1137
The code is working correctly, as @m_dubya said there is a total of 3s sleep between the append
in place_order
and the pop
in serve_order
.
For example if you change the sleep
in the place_order
function to 2 seconds the result will be:
Ordered: pizza
Ordered: samosa
Served: pizza
Ordered: pasta
Served: samosa
Ordered: biryani
Served: pasta
Ordered: burger
Served: biryani
Served: burger
Upvotes: 0