今夜の注文書
#01 · 一日目の夜
ピンチ
店を開けたとたんに客が8組。副料理長が一人で注文をひとつずつ作る。スープが煮えるあいだ、ただ突っ立って待っている。
目標
8つの注文を1秒以内に出す。
深夜食堂 ドンシ
開店初日の夜10時。扉を開けたとたん、客がどっと押し寄せます。あなたは最初の注文のスープを火にかけ、煮え立つまで鍋の前に立っています。3分後に1杯目を出し、2つ目の注文に取りかかります。
ユン・ソヨンシェフがタイマーを見ながら言います。「スープが煮えてるあいだ、その手は何してた? 何もしてないよね。それが今夜の問題」
スープが煮えるのを待つ仕事を、Pythonでまねてみましょう。time.sleep(0.3) は「0.3秒間なにもせずに待つ」という意味です。3分を0.3秒に縮めたと考えてください。
import time
def make_order(n):
time.sleep(0.3) # スープが煮えるのを待つ
return f"#{n}"
start = time.perf_counter()
done = [make_order(n) for n in range(1, 9)]
print(f"{time.perf_counter() - start:.2f}s", done)
# 2.40s ['#1', '#2', '#3', '#4', '#5', '#6', '#7', '#8']time.perf_counter() は時間を測るための時計です。2回読んで引き算すると、そのあいだに経った秒数が出ます。実行するたびに下の桁は少しずつ変わることがあります。時間を測る例の結果は、いつでも「だいたい」です。
厨房の仕事は2種類に分かれます。
| 待つ仕事 (I/O) | 手がふさがる仕事 (CPU) | |
|---|---|---|
| 厨房では | スープを煮る、オーブンで焼く、配達を待つ | 生地をこねる、千切りにする |
| プログラムでは | ネットワークの応答、ファイルの読み込み、データベース | 計算、圧縮、画像処理 |
| 待っているあいだ | 人は手が空いている | 人はずっと忙しい |
並行処理で時間が大きく縮むのは、待つ仕事が多いときです。スープが煮えているあいだに別の鍋をかければいいのですから。手がふさがる仕事は実際に人手を増やさないと速くなりませんが、Pythonにはここに隠れたルールがひとつあります。その話は8限目でします。
この2つの言葉はよく混同されますが、意味が違います。
並列処理は並行処理を実現する方法のひとつです。待つ仕事ばかりの厨房なら、手がひとつでも並行処理だけで十分速くなります。下のタイムラインで「一人で順番に」と「料理人を増やす」を切り替えながら回してみてください。バー1本が注文ひとつで、横軸が時間です。
厨房タイムライン
注文
6
料理人
3
調理
待ち
キャンセル
失敗
time.sleep し、各注文が実際に始まって終わった時刻を記録して描いたものです。注文がすべて同じ長さなら、かかる時間は「一人の料理人が受け持つ最大の注文数 × 注文ひとつの時間」です。
import math
def estimate(orders, cooks, each=0.3):
rounds = math.ceil(orders / cooks) # 一人の料理人が受け持つ最大の注文数
return round(rounds * each, 2)
for cooks in (1, 3, 4, 8, 16):
print(cooks, estimate(8, cooks))
# 1 2.4
# 3 0.9
# 4 0.6
# 8 0.3
# 16 0.3Pythonには、複数の仕事を同時にこなす道具が3つあります。今日は味見だけして、次の限目からひとつずつ深く掘り下げます。
import threading
threads = [threading.Thread(target=make_order, args=(n,)) for n in range(1, 9)]
for th in threads:
th.start() # 料理人8人が同時にスタート
for th in threads:
th.join() # 全員が終わるまで待つ
# 約0.30秒import asyncio
async def make_order_async(n):
await asyncio.sleep(0.3) # 待っているあいだに別の注文を見る
return f"#{n}"
async def main():
return await asyncio.gather(*(make_order_async(n) for n in range(1, 9)))
print(asyncio.run(main()))
# ['#1', '#2', '#3', '#4', '#5', '#6', '#7', '#8'] (約0.31秒)| 道具 | 厨房でいえば | 向いている仕事 | 限目 |
|---|---|---|---|
threading | 同じ厨房に料理人を何人も | 待つ仕事、古いコード | 2〜7 |
multiprocessing | 冷蔵庫まで別々の支店の厨房 | 手がふさがる仕事(CPU) | 8 |
asyncio | 一人で何十卓も回すホールマネージャー | 待つ仕事がとても多いとき | 9〜11 |
シェフのひとこと
料理人を増やせば速くなる、それは本当。ただし厨房が騒がしくなる。二人が同じ黒板に同時に数字を書いたらどうなると思う? それは3限目に自分の目で見られるよ。
料理人を増やすにはコストがかかります。
今夜の目標は、8つの注文を1秒以内に出すことでした。注文はすべて「待つ仕事」なので、料理人8人がひとつずつ受け持てばいいわけです。
start = time.perf_counter()
threads = [threading.Thread(target=make_order, args=(n,)) for n in range(1, 9)]
for th in threads:
th.start()
for th in threads:
th.join()
print(f"{time.perf_counter() - start:.2f}s")
# 0.30sシェフがタイマーを止めます。「2.4秒から0.3秒。初日にしては悪くないね。でも料理人が8人になったら、誰が何個作ったかは誰が数えるの?」
営業日誌 — 一日目の夜。注文8件がすべて待つ仕事だったので、料理人8人で手分けして0.3秒で完了。明日は料理人を正式に雇う。
make_order の time.sleep(0.3) を数を足し続けるループ(手がふさがる仕事)に置き換え、スレッドで回すと速くなるか測ってみましょう。結果がおかしいと思ったら、その理由は8限目でわかります。シェフのレシピカード
待つ仕事(I/O)は並行処理で大きく速くなる。手がふさがる仕事(CPU)には本当の並列が必要。
並行 = かわるがわる進める、並列 = 同じ瞬間に実行する。
時間の測り方:start = time.perf_counter() … time.perf_counter() - start
道具は3つ:threading(料理人を何人も)、multiprocessing(厨房をいくつも)、asyncio(一人で回すホール)。
並行処理はタダじゃない:管理のコストと新しいバグがついてくる。
新しい授業・講座のお知らせを受け取る
この講座に授業が追加されたり、続きの講座が公開されたりしたらメールでお知らせします。
R
ReflexKR
触って学ぶPythonとReflex。すべての例はReflex 0.10.0で実際に動かして確認しています。
ReflexKRはバナー広告なし・無料で運営しています。役に立ったら、コーヒー一杯で応援してください。 ☕ コーヒーで応援する
アフィリエイトと支援について© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
質問と回答
この回について気になることを書き込んでください。すべての言語のページの質問がここに集まります。