Introduction#
About#
Python is a high level program language, but it could implement different parallelism: Parallel, Concurrent and Coroutine. No matter which way developers choose to use, they all have some rules:
Initial multiple runnable objects -> Run them -> Join and Close them
About runnable objects, it means an object which would be configured target functions (sometimes, including its parameters). Developers could initial multiple instances of it and run them simultaneously. Like threading.Thread or multiprocessing.Process. No matter Parallel, Concurrent or Coroutine, they’re different, absolutely. But their usage-procedure are similar, too. MultiRunnable target to integrate the usage-procedure and try to let developers to implement parallelism code easily and clearly with different running strategy, event it still isn’t a stable and entire package (It’s release version is 0.17.0) currently.
Comparison#
Now, it clears that Parallel, Concurrent or Coroutine are different but its usage-procedures are mostly the same. Below are some demonstrations with them:
Parallel - with ‘multiprocessing’:
from multiprocessing import Process
Process_Number = 5
def function():
print("This is test process function.")
if __name__ == '__main__':
# Initial runnable objects --- Processes
process_list = [Process(target=function)for _ in range(Process_Number)]
# Run runnable objects
for p in process_list:
p.start()
# Close runnable objects
for p in process_list:
p.join()
p.close() # New in Python 3.7 up.
Concurrent - with ‘threading’:
import threading
Thread_Number = 5
def function():
print("This is function content ...")
if __name__ == '__main__':
# Initial runnable objects --- Threads
threads_list = [threading.Thread(target=function) for _ in range(Thread_Number)]
# Run runnable objects
for thread in threads_list:
thread.start()
# Close runnable objects
for thread in threads_list:
thread.join()
thread.close()
Coroutine - with ‘gevent’ (Green Thread):
from gevent import Greenlet
Green_Thread_Number = 5
def function():
print("This is function content ...")
if __name__ == '__main__':
# Initial runnable objects --- Green Threads
greenlets_list = [Greenlet(function) for _ in range(Green_Thread_Number)]
# Run runnable objects
for _greenlet in greenlets_list:
_greenlet.start()
# Close runnable objects
for _greenlet in greenlets_list:
_greenlet.join()
Coroutine - with ‘asyncio’ (Asynchronous):
import asyncio
async def function():
print("This is function content ...")
async def running_function():
# Initial runnable objects --- Green Threads
task = asyncio.create_task(function())
await task
if __name__ == '__main__':
# Run runnable objects
asyncio.run(running_function())
Above all are demonstrations with Python library (multiprocessing, threading and asyncio are native library). It could observe that the usage-procedures of Parallel (multiprocessing), Concurrent (threading), Coroutine (gevent) are the same. Though left one Coroutine (asyncio) doesn’t need to close asynchronous tasks but it still needs to initial and run them. So let’s show an example parallelism code with multirunnable:
Implement by ‘multirunnable’:
from multirunnable import SimpleExecutor, RunningMode
import random
import time
Workers_Number = 5
def function(index):
print(f"This is function with index {index}")
time.sleep(3)
if __name__ == '__main__':
executor = SimpleExecutor(mode=RunningMode.Concurrent, executors=Workers_Number)
executor.run(function=function, args={"index": f"test_{random.randrange(1, 10)}"})
Obviously, you could reach the same target with using Executor.run only. However, that’s Concurrent. How about Parallel or Coroutine? It’s very easy, it just only change the value of option mode:
... # Any code is the same
# Change to Parallel!
executor = SimpleExecutor(mode=RunningMode.Parallel, executors=Workers_Number)
... # Any code is the same
or
... # Any code is the same
# Change to Coroutine with Green Thread!
executor = SimpleExecutor(mode=RunningMode.GreenThread, executors=Workers_Number)
... # Any code is the same
It’s also the same with Coroutine — Asynchronous. But please remember that target function should be a awaitable function:
from multirunnable import SimpleExecutor, RunningMode, async_sleep
import random
Workers_Number = 5
async def function(index):
print(f"This is function with index {index}")
async_sleep(3)
if __name__ == '__main__':
executor = SimpleExecutor(mode=RunningMode.Asynchronous, executors=Workers_Number)
executor.run(function=function, args={"index": f"test_{random.randrange(1, 10)}"})
Simple Demonstration#
Install this package by pip:
pip install multirunnable
Write a simple code to run it.
>>> from multirunnable import SimpleExecutor, RunningMode
>>> executor = SimpleExecutor(mode=RunningMode.Parallel, executors=3)
>>> def function(index):
... print(f"This is function with index {index}")
...
>>> executor.run(function=function, args={"index": f"test_param"})
This is function with index test_param
This is function with index test_param
This is function with index test_param
>>>