'Python How to make function run simultaneously using asyncio
I am currently new to Python and wanted to create a system that takes 10 customers that come in at an interval of 1 - 25 seconds. They are sent to a queue, and the customers in front of the queue will be sent to one of the four open counters. The counter will service them for 1-100 seconds and record each customer's start time and end time of service. I wanted to run the customer entering and the counter service simultaneously using asyncio but am unable to make them run simultaneously as they run in order. My question is, how can I make my code able to run these two functions simultaneously? My code:
import asyncio
from random import randint
import collections
import time
#create counter true false
Counter_List = {0: True, 1: True, 2: True, 3: True}
#hash table
served = collections.defaultdict(list)
#empty que list
que_list = collections.deque([])
#global variable
total_customer = 0
counter = 0
start_time = time.time()
#event loop
#interarrival time
async def interarrival_time():
global total_customer
while total_customer < 10:
print('customer ' + str(total_customer) + ' entered')
time.sleep(randint(1,25))
#que to right side
que_list.append(total_customer)
total_customer += 1
#service time
#start service
async def service_start():
global counter, total_customer, start_time
while counter <= 4 and len(que_list) > 0:
for x in Counter_List:
if Counter_List[x] == True:
#set to false the current counter
Counter_List[x] = False
counter += 1
print('counter ' + str(x) + ' available')
#que list
customerid = que_list[0]
#pop left of the quelist
que_list.popleft()
#get customer in que, record counter, start time
served[x].append('customer ' + str(customerid))
served[x].append('Start time: ' + str(int(time.time() - start_time)))
await asyncio.sleep(randint(1,100))
#record end time
served[x].append('End time: ' + str(int(time.time() - start_time)))
#set counter to true
Counter_List[x] = True
counter -= 1
if total_customer == 10 and len(que_list) == 0:
#print the dictionary
print (served)
loop.stop()
loop = asyncio.get_event_loop()
try:
asyncio.ensure_future(interarrival_time())
asyncio.ensure_future(service_start())
loop.run_forever()
finally:
loop.close()
My output:
customer 0 entered
customer 1 entered
customer 2 entered
customer 3 entered
customer 4 entered
customer 5 entered
customer 6 entered
customer 7 entered
customer 8 entered
customer 9 entered
counter 0 available
counter 1 available
counter 2 available
counter 3 available
counter 0 available
counter 1 available
counter 2 available
counter 3 available
counter 0 available
counter 1 available
defaultdict(<class 'list'>, {0: ['customer 0', 'Start time: 134', 'End time: 183', 'customer 4', 'Start time: 327', 'End time: 421', 'customer 8', 'Start time: 562', 'End time: 651'], 1: ['customer 1', 'Start time: 183', 'End time: 201', 'customer 5', 'Start time: 421', 'End time: 454', 'customer 9', 'Start time: 651', 'End time: 656'], 2: ['customer 2', 'Start time: 201', 'End time: 267', 'customer 6', 'Start time: 454', 'End time: 538'], 3: ['customer 3', 'Start time: 267', 'End time: 327', 'customer 7', 'Start time: 538', 'End time: 562']})
The 'customer x entered' and 'counter x available' are ways to keep track of if the system is running simultaneously. I would like it so that it will be as an example:
customer 1 entered
counter 0 available
customer 2 entered
counter 1 available
customer 3 entered
counter 2 available
customer 4 entered
counter 0 available
customer 5 entered
counter 3 available
customer 6 entered
customer 7 entered
counter 2 available
and so on
Solution 1:[1]
What you want is asyncio.Queue
for consumer-producer types of flows. Whenever a counter
is free, it simply needs to ask the queue
for the next customer
.
import asyncio
import random
async def counter_task(customers: asyncio.Queue, counter_id: int) -> None:
while True:
# Get the next available customer.
customer = await customers.get()
print(f"Customer {customer} is being serviced at counter {counter_id}.")
# Work on that customer.
await asyncio.sleep(random.randint(1, 100))
print(f"Customer {customer} has left counter {counter_id}.")
# Signal the customer is done.
customers.task_done()
async def main() -> None:
customers = asyncio.Queue()
counters = [asyncio.create_task(counter_task(customers, i)) for i in range(4)]
try:
# Create 10 customers.
for customer in range(10):
print(f"Customer {customer} has entered.")
await customers.put(customer)
await asyncio.sleep(random.randint(1, 25))
# Wait for them to be signalled as finished.
await customers.join()
finally:
# Stop the counters.
for counter in counters:
counter.cancel()
for counter in counters:
try:
await counter
except asyncio.CancelledError:
pass
asyncio.run(main())
Sample output:
Customer 0 has entered.
Customer 0 is being serviced at counter 0.
Customer 1 has entered.
Customer 1 is being serviced at counter 1.
Customer 2 has entered.
Customer 2 is being serviced at counter 2.
Customer 1 has left counter 1.
Customer 3 has entered.
Customer 3 is being serviced at counter 3.
Customer 4 has entered.
Customer 4 is being serviced at counter 1.
Customer 5 has entered.
Customer 0 has left counter 0.
Customer 5 is being serviced at counter 0.
Customer 6 has entered.
Customer 7 has entered.
Customer 4 has left counter 1.
Customer 6 is being serviced at counter 1.
Customer 8 has entered.
Customer 2 has left counter 2.
Customer 7 is being serviced at counter 2.
Customer 9 has entered.
Customer 3 has left counter 3.
Customer 8 is being serviced at counter 3.
Customer 7 has left counter 2.
Customer 9 is being serviced at counter 2.
Customer 5 has left counter 0.
Customer 6 has left counter 1.
Customer 8 has left counter 3.
Customer 9 has left counter 2.
Solution 2:[2]
If you don't care about overhead and memory in your app(it is not heavy) use Multi Threading. Although asyncio has the advantage of less overhead, but the codes which you use to write an asyncio program is very specific and it is very hard to use them in another app. For instance you can see in API frameworks, the developers develop the framework in 2 ways, ASGI(asyncio) and WSGI(not async)!!!!
For example: https://falcon.readthedocs.io/en/stable/user/index.html
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Simply Beautiful Art |
Solution 2 | Sina Karimi |