Python並行処理:注意すべきポイント
Pythonの並行処理における複雑な部分について、GILやスレッド・プロセスの違い、非同期処理の落とし穴などを解説。
キーポイント
Pythonの並行処理(スレッド、プロセス、コルーチン)の違いを実践的に解説
David Beazleyのライブコーディング講演を基にした教育コンテンツとしての価値
データサイエンティスト向けにソケットプログラミングを用いた具体的なデモンストレーション
Fibonacci計算とWebサーバーを例にした並行処理の実装パターン
影響分析・編集コメントを表示
影響分析
この記事はPython開発者、特にデータサイエンスからソフトウェアエンジニアリングに移行する層にとって、並行処理の実践的理解を深める重要な教材となる。抽象的な概念説明ではなく、具体的なコード例を通じて学習効果を高めており、AIシステムの効率化やスケーリングに直接関連する技術知識を提供している。
編集コメント
AIシステム開発において並行処理の理解はパフォーマンス最適化に不可欠であり、データサイエンティストのキャリア拡大にも役立つ実践的な内容。教育アプローチが秀逸で、技術習得の障壁を下げる良質な記事。
Pythonにおける並行処理の複雑なポイント:スレッド、プロセス、コルーチンの実践的解説
データサイエンティストからソフトウェアエンジニアリングに比重を移す中で、Pythonの並行処理に関する知識の大きな隔たりに直面した。async、スレッド、プール、コルーチンといった用語の違いや、これらの機構がどう連携するか、具体的に理解できていなかった。抽象的な例が多く、実態を把握するのは難しかったが、Python教育の第一人者であるDavid Beazleyのライブコーディング講演を通じて、状況は一変した。
この講演は、ソケットプログラミングというデータサイエンティストには馴染みのない領域から始まり、当初は圧倒される内容だ。しかし、各構成要素を丁寧に追うことで、Python並行処理に関する最高の教材となる。本記事は、その学びの道筋を記録したものである。
デモンストレーションのため、二つの基盤が用意された。第一に、CPUを顕著に飽和させるタスクとして、フィボナッチ数を計算する再帰関数が用いられる。入力値が大きくなるほど処理時間が長くなる特性を活かし、様々なワークロードをプロファイリングする。
第二に、並行処理の違いを明確に示す最適な例として、ウェブサーバーが構築される。ただし、内部の動作を詳細に観察できるよう、低レベルなソケットプログラミングが採用された。最初のサーバーコードは、ソケットを作成し、接続を待ち受け、クライアントからの要求を受信してフィボナッチ数を計算し、結果を返すという、基本的で逐次処理の構造を持つ。この単純な実装は、同時に複数のリクエストを処理できないという明らかな限界を持ち、これが並行処理の必要性を浮き彫りにする起点となる。
講演では、この単純なサーバーを出発点として、スレッド、マルチプロセス、そして非同期I/O(async/await)とコルーチンを用いた並行処理モデルへと、段階的に発展させていく。各アプローチが、グローバルインタプリタロック(GIL)の制約、CPUバウンド/ I/Oバウンドタスクへの適性、オーバーヘッド、コードの複雑さといった点でどのように異なるかを、同一のサーバータスクを通じて実践的かつ対比的に明らかにする。
要するに、この探求の核心は、Pythonにおける並行処理の各パラダイム(スレッド、プロセス、コルーチン)の本質的な違いと、それらが具体的にどのように動作し、いつ使い分けるべきかを、生きたコードと具体的なサーバー例を通じて解き明かす点にある。抽象的な理論ではなく、実際の動作とパフォーマンス特性を観察することで、これらの「厄介な部分」を確実に理解することができるのである。
原文を表示
An exploration of threads, processes, and coroutines in Python, with interesting examples that illuminate the differences between each.
As a data scientist who is spending more time on software engineering, I was recently forced to confront an ugly gap in my knowledge of Python: concurrency. To be honest, I never completely understood how the terms async, threads, pools and coroutines were different and how these mechanisms could work together. Every time I tried to learn about the subject, the examples were a bit too abstract for me, and I hard time internalizing how everything worked.
This changed when a friend of mine2 recommended a live coding talk by David Beazley, an accomplished Python educator.
Because of restrictions with this YouTube video, I couldn’t embed the video in this article, so you will have to open it in a different window.
This talk is incredibly intimidating at first. Not only is it coded live from scratch, but it also jumps immediately into socket programming, something that I had never encountered as a data scientist. However, if you go through it slowly and understand all the components (as we do in this blog post) it turns out to be the best educational material on Python concurrency I have ever encountered. This blog post documents what I learned along the way so others can benefit, too.
Before getting started, David sets up the following infrastructure that is used to demonstrate concurrency.
To demonstrate concurrency, it is useful to create a task that can saturate your CPU (such as mathematical operations) for a noticeable period of time. David uses a function that computes a Fibonacci number.
1 2 3 4 #fib.py def fib(n): if n <= 2: return 1 else: return fib(n-1) + fib(n-2) This function takes much longer for large inputs versus smaller inputs3, which allows us to profile different workloads.
A web server is one of the best ways to illustrate different types of concurrency. However, to really demonstrate how things work it is useful to use something that is sufficiently low level enough to see how all the pieces work. For this, David sets up a web server using socket programming. If you aren’t familiar with socket programming, I’ll explain the important bits below, but feel free to dive deeper with this tutorial later if you like.
To begin with, David starts with the below code (I’ve highlighted the most interesting bits):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # server-1.py from socket import * from fib import fib def fib_server(address): sock = socket(AF_INET, SOCK_STREAM) sock.setsockopt(SOL_SOCKET, SO_REUSEADDR,1) sock.bind(address) sock.listen(5) while True: client,addr = sock.accept() # waits for a connection to be established print("Connection", addr) fib_handler(client) # passes the client to a handler which will listen for input data. def fib_handler(client): while True: req = client.recv(100) # waits for data that sent by the client. if not req: break result = fib(int(req)) resp = str(result).encode('ascii') + b'\n' client.send(resp) # sends data back to the client. print("Closed") fib_server(('', 25000)) Here is an explanation of this code:
Lines 6-9 are socket programming boilerplate. It’s ok to just take this for granted as a reasonable way to set up a socket server. This also matches the the tutorial I linked to above.
Line 11 waits for an incoming connection from a client. Once a connection is made, the server can begin receiving data from a client. The code will stop execution on this line until a connection is made.
Line 13: Once a connection is established, the client object is passed to a function which can handle data sent by the client.
Line 17: waits for data to be sent by the client. The code will stop execution on this line until data is received from the client.
Line 21: The server sends a response back to the client. The code could stop execution on this line if the send buffers are full, but unlikely in this toy example.
Testing the non-concurrent code
In the above example, the server will only be able to accept a connection from a single client, because the call to fib_handler will never return (because it will run in an infinite loop unless a kill signal is received). This means that sock.accept() can only be called once.
You can test this out by first running the server:
1 python server-1.py Then establish a client:
1 telnet localhost 25000 You can type numbers in as David does in his video and verifies that fibonacci numbers are returned. However, if you try to connect with another client at the same time from a different terminal session:
1 telnet localhost 25000 You will notice that the second client just hangs and doesn’t return anything from the server. This is because the server is only able to accept a single connection. Next, we explore how to tackle this issue.
We can solve this issue with threads. You can add threads to the handler so that mo
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み