Pythonの並列・並行処理サンプルコードまとめ

Pythonで同時に2つ以上の処理をする方法を紹介します

- スレッド
- スレッドプール
- プロセスプール
- イベントループ（コルーチン）

スレッド (threading)
=============================
スレッドを使えば、複数の関数を同時に動かすことができます。
`threading.Thread` クラスに `target` として関数を渡し、`start()` で開始すると動きます。

```py
import time
import threading


def func1():
    while True:
        print("func1")
        time.sleep(1)


def func2():
    while True:
        print("func2")
        time.sleep(1)


if __name__ == "__main__":
    thread_1 = threading.Thread(target=func1)
    thread_2 = threading.Thread(target=func2)

    thread_1.start()
    thread_2.start()

```

実行結果

```
func1
func2
func2
func1
func1
func2
func2
func1
```


スレッドプール (concurrent.futures)
========================================
Python 3.2以降の、[concurrent.futures パッケージ](https://docs.python.jp/3/library/concurrent.futures.html#module-concurrent.futures) を使うとさらに強力です。
その中の [ThreadPoolExecutor クラス](https://docs.python.jp/3/library/concurrent.futures.html#threadpoolexecutor) を使います。


最初に同時に動かす最大数 `max_workers` を決めるとスレッドを使いまわしてくれるので上で紹介した普通のスレッドよりかしこいです。
Pythonのバージョンが新しいならば、積極的に使っていいと思います。

```py
import time
import concurrent.futures


def func1():
    while True:
        print("func1")
        time.sleep(1)


def func2():
    while True:
        print("func2")
        time.sleep(1)


if __name__ == "__main__":
    executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)
    executor.submit(func1)
    executor.submit(func2)
```

実行結果

```
func1
func2
func1
func2
func1
func2
func1
func2
```


プロセスプール (concurrent.futures)
=====================================
上に同じく [concurrent.futures パッケージ](https://docs.python.jp/3/library/concurrent.futures.html#module-concurrent.futures) ですが、スレッドプールではなく**プロセスプール**というのもあります。

スレッドではなくプロセス単位に分けることで、[Global Interpreter Lock (GIL) の制約](https://docs.python.jp/3/glossary.html#term-global-interpreter-lock)を受けなくなり**マルチコアで動かせるようになります。**
ただし、その分スレッドよりも規模が大きいプロセスを使うので、他の制約が増えることも。注意！

使い方は簡単で、上で紹介した `ThreadPoolExecutor` を `ProcessPoolExecutor` に変えるだけです。

```py
import time
import concurrent.futures


def func1():
    while True:
        print("func1")
        time.sleep(1)


def func2():
    while True:
        print("func2")
        time.sleep(1)


if __name__ == "__main__":
    executor = concurrent.futures.ProcessPoolExecutor(max_workers=2)
    executor.submit(func1)
    executor.submit(func2)
```

実行結果

```
func1
func2
func1
func2
func1
func2
func1
func2
```


イベントループ（コルーチン）
================================
ひとつのスレッドで複数の処理を動かす方法もあります。そのひとつが**イベントループ**です。
Python3.4以降では [asyncio モジュール](https://docs.python.jp/3/library/asyncio.html#module-asyncio) で実現できます。

マルチスレッドとの違い、どのような場合に使うと良いか…といったお話は [Pythonにおける非同期処理: asyncio逆引きリファレンス](http://qiita.com/icoxfog417/items/07cbf5110ca82629aca0) を読むとわかりやすいです。

通信やファイル入出力といった非同期I/Oあたりにはスレッドを増やすよりも非常に効率的なのですが、概念が難しいのでなれるまで大変です・・。

サンプルコードはスレッドの場合とは結構違います。

待機する処理に `time.sleep` ではなく `asyncio.sleep` を使っていますが、これは `asyncio.sleep` を呼び出して待っている間に他の並列している処理に移動するためです。まさにコルーチンですね。

```py
import asyncio


@asyncio.coroutine
def func1():
    while True:
        print("func1")
        yield from asyncio.sleep(1)


@asyncio.coroutine
def func2():
    while True:
        print("func2")
        yield from asyncio.sleep(1)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    tasks = asyncio.wait([func1(), func2()])
    loop.run_until_complete(tasks)
```

実行結果

```
func2
func1
func2
func1
func2
func1
func2
func1
```