tech:notes_python_multithreading
Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
| tech:notes_python_multithreading [2025/05/20 17:45] – Jean-Baptiste | tech:notes_python_multithreading [2026/06/07 19:12] (Version actuelle) – modification externe 127.0.0.1 | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| + | < | ||
| + | {{tag> | ||
| + | |||
| + | # Notes Python multithreading | ||
| + | |||
| + | Voir : | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * asyncio / aiohttp / multiprocess / trio / grequests | ||
| + | |||
| + | Équivalent à `defer` (go lang) : | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | |||
| + | |||
| + | Thread safe : | ||
| + | * https:// | ||
| + | * https:// | ||
| + | |||
| + | |||
| + | |||
| + | Nombre de CPU logiques | ||
| + | ~~~python | ||
| + | import multiprocessing | ||
| + | |||
| + | multiprocessing.cpu_count() | ||
| + | ~~~ | ||
| + | |||
| + | ## Exemples | ||
| + | |||
| + | ~~~python | ||
| + | from threading import Thread | ||
| + | import requests | ||
| + | |||
| + | THREAD_COUNT = 6 | ||
| + | |||
| + | def callback(): | ||
| + | try: | ||
| + | while True: | ||
| + | r = requests.get(' | ||
| + | print(r) | ||
| + | except KeyboardInterrupt: | ||
| + | return | ||
| + | |||
| + | if __name__ == ' | ||
| + | threads = [] | ||
| + | for i in range(THREAD_COUNT): | ||
| + | t = Thread(target=callback) | ||
| + | threads.append(t) | ||
| + | t.start() | ||
| + | |||
| + | for t in threads: | ||
| + | t.join() | ||
| + | ~~~ | ||
| + | |||
| + | Source : https:// | ||
| + | |||
| + | ~~~python | ||
| + | import requests | ||
| + | import threading | ||
| + | def make_request(url): | ||
| + | response = requests.get(url) | ||
| + | print(f" | ||
| + | # List of URLs to make requests to | ||
| + | urls = [ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | ] | ||
| + | # Create and start threads for each URL | ||
| + | threads = [] | ||
| + | for url in urls: | ||
| + | thread = threading.Thread(target=make_request, | ||
| + | thread.start() | ||
| + | threads.append(thread) | ||
| + | # Wait for all threads to finish | ||
| + | for thread in threads: | ||
| + | thread.join() | ||
| + | ~~~ | ||
| + | |||
| + | Source : https:// | ||
| + | |||
| + | |||
| + | ~~~python | ||
| + | import grequests | ||
| + | |||
| + | class Test: | ||
| + | def __init__(self): | ||
| + | self.urls = [ | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ] | ||
| + | |||
| + | def exception(self, | ||
| + | print " | ||
| + | |||
| + | def async(self): | ||
| + | results = grequests.map((grequests.get(u) for u in self.urls), exception_handler=self.exception, | ||
| + | print results | ||
| + | |||
| + | test = Test() | ||
| + | test.async() | ||
| + | ~~~ | ||
| + | |||
| + | Source : https:// | ||
| + | |||
| + | |||
| + | ## Alternatives Multi threads à map | ||
| + | |||
| + | ### Exemple 1 | ||
| + | |||
| + | Voir : | ||
| + | * https:// | ||
| + | * https:// | ||
| + | |||
| + | ~~~python | ||
| + | import concurrent.futures | ||
| + | |||
| + | with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: | ||
| + | executor.map(get_dataset, | ||
| + | ~~~ | ||
| + | |||
| + | ou encore | ||
| + | ~~~python | ||
| + | from multiprocessing import Pool | ||
| + | |||
| + | p = Pool(12) | ||
| + | p.map(process_archive, | ||
| + | ~~~ | ||
| + | |||
| + | Voir : https:// | ||
| + | |||
| + | |||
| + | ### Exemple 2 | ||
| + | |||
| + | |||
| + | ~~~python | ||
| + | from multiprocessing import Pool | ||
| + | |||
| + | pool = Pool() | ||
| + | result1 = pool.apply_async(solve1, | ||
| + | result2 = pool.apply_async(solve2, | ||
| + | answer1 = result1.get(timeout=10) | ||
| + | answer2 = result2.get(timeout=10) | ||
| + | ~~~ | ||
| + | |||
| + | ~~~python | ||
| + | args = [A, B] | ||
| + | results = pool.map(solve1, | ||
| + | ~~~ | ||
| + | |||
| + | Source : https:// | ||
| + | |||
| + | ### Exemple 3 | ||
| + | |||
| + | You can apply the function to every element in a list using the map() function: | ||
| + | ~~~python | ||
| + | list(map(square, | ||
| + | ~~~ | ||
| + | |||
| + | The multiprocessing.pool.Pool class provides an equivalent but parallelized (via multiprocessing) way of doing this. The pool class, by default, creates one new process per CPU and does parallel calculations on the list: | ||
| + | |||
| + | ~~~python | ||
| + | from multiprocessing import Pool | ||
| + | |||
| + | with Pool() as pool: | ||
| + | |||
| + | pool.map(square, | ||
| + | ~~~ | ||
| + | |||
| + | Source : https:// | ||
| + | |||
| + | ------------------ | ||
| + | |||
| + | |||
| + | ## Notes async asyncio await | ||
| + | |||
| + | Voir : | ||
| + | * [[Python - callback]] | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | |||
| + | Async Queues : | ||
| + | * https:// | ||
| + | |||
| + | |||
| + | |||
| + | A savoir : | ||
| + | * Vous pouvez uniquement utiliser `await` dans les fonctions créées avec `async def` | ||
| + | * Une Coroutune déclarée avec `async def` ne peut être appelée que par | ||
| + | * `await` si le résultat doit être attendu | ||
| + | * `create_task()` (`asyncio.create_task()` ou `asyncio.TaskGroup`) pour ne pas attendre le résultat | ||
| + | * `asyncio.gather()` | ||
| + | * `asyncio.run()` | ||
| + | * " | ||
| + | * `time.sleep()` (ainsi que d' | ||
| + | |||
| + | |||
| + | ### Sleep | ||
| + | |||
| + | An important use for sleep in asyncio programs is to suspend the current task and allow other coroutines to execute. | ||
| + | |||
| + | It is important because although a task or coroutine can easily schedule new tasks via the create_task() or gather() function, the scheduled tasks will not begin executing until the current task is suspended. | ||
| + | |||
| + | Even sleeping for zero seconds is enough to suspend the current task and give an opportunity to other tasks to run. | ||
| + | |||
| + | For example: | ||
| + | ~~~python | ||
| + | # allow other tasks to run for a moment | ||
| + | await asyncio.sleep(0) | ||
| + | ~~~ | ||
| + | |||
| + | Finally, a good use for sleep is to simulate blocking tasks in a concurrent program. | ||
| + | |||
| + | |||
| + | Source : https:// | ||
| + | |||
| + | ### Autres | ||
| + | |||
| + | Ne pas quiter avant la fin du traitement des tâches | ||
| + | ~~~python | ||
| + | async def main(): | ||
| + | # Create some tasks. | ||
| + | for _ in range(10): | ||
| + | asyncio.create_task(asyncio.sleep(10)) | ||
| + | # Wait for all other tasks to finish other than the current task i.e. main(). | ||
| + | await asyncio.gather(*asyncio.all_tasks() - {asyncio.current_task()}) | ||
| + | ~~~ | ||
| + | Source : https:// | ||
| + | |||
| + | |||
| + | asyncio & threading | ||
| + | ~~~python | ||
| + | import asyncio | ||
| + | import threading | ||
| + | |||
| + | async def something_async(): | ||
| + | print(' | ||
| + | await asyncio.sleep(1) | ||
| + | print(' | ||
| + | |||
| + | def main(): | ||
| + | t1 = threading.Thread(target=asyncio.run, | ||
| + | t2 = threading.Thread(target=asyncio.run, | ||
| + | t1.start() | ||
| + | t2.start() | ||
| + | t1.join() | ||
| + | t2.join() | ||
| + | |||
| + | if __name__ == ' | ||
| + | main() | ||
| + | ~~~ | ||
| + | Source : https:// | ||
| + | | ||
| + | Autre | ||
| + | ~~~python | ||
| + | import time | ||
| + | from random import randint | ||
| + | |||
| + | period = 1 # Second | ||
| + | |||
| + | def get_epoch_ms(): | ||
| + | return int(time.time() * 1000.0) | ||
| + | |||
| + | async def do_something(name): | ||
| + | print(" | ||
| + | try: | ||
| + | # Do something which may takes more than 1 secs. | ||
| + | slp = randint(1, 5) | ||
| + | print(" | ||
| + | await asyncio.sleep(slp) | ||
| + | except Exception as e: | ||
| + | print(" | ||
| + | |||
| + | print(" | ||
| + | |||
| + | loop = asyncio.get_event_loop() | ||
| + | futures = [loop.create_task(do_something(' | ||
| + | for i in range(5)] | ||
| + | |||
| + | # | ||
| + | |||
| + | #for f in futures: | ||
| + | # f.cancel() | ||
| + | |||
| + | for f in futures: | ||
| + | | ||
| + | ~~~ | ||
| + | Source : https:// | ||
| + | |||
| + | Thread safe | ||
| + | |||
| + | ~~~python | ||
| + | from collections import deque | ||
| + | |||
| + | thread_safe_deque = deque() | ||
| + | |||
| + | # Thread 1 | ||
| + | thread_safe_deque.append(1) | ||
| + | |||
| + | # Thread 2 | ||
| + | element = thread_safe_deque.pop() | ||
| + | ~~~ | ||
| + | |||
| + | |||
| + | Source : https:// | ||
| + | |||
| + | --------- | ||
| + | |||
| + | Source : nsclient `NSCP-0.8.0-x64/ | ||
| + | |||
| + | `badapp.py` | ||
| + | ~~~python | ||
| + | #! / | ||
| + | |||
| + | import threading | ||
| + | |||
| + | class BadThread(threading.Thread): | ||
| + | id = -1 | ||
| + | def __init__(self, | ||
| + | self.id = id | ||
| + | threading.Thread.__init__(self) | ||
| + | |||
| + | def run(self): | ||
| + | i = 0 | ||
| + | while(True): | ||
| + | i = i + 1 | ||
| + | if i > 100000: | ||
| + | print(' | ||
| + | i = 0 | ||
| + | |||
| + | for x in range(1000): | ||
| + | BadThread(x).start() | ||
| + | ~~~ | ||
| + | |||
