Pythonでリストをスレッドセーフに操作する方法

この記事では、Pythonでリストをスレッドセーフに操作する方法について詳しく解説します。Pythonの標準ライブラリである`threading`を用いた基本的なコードから、より高度なテクニックまで、具体的なコード例とその解説、応用例を含めています。

目次

なぜスレッドセーフが重要なのか

多くのプログラムでは、複数のスレッドが同時にリストにアクセスする場面があります。しかし、Pythonのリストはデフォルトでスレッドセーフではないため、データの破損や不整合が発生する可能性があります。この記事では、その問題を解決するためのテクニックと応用例を紹介します。

基本的なスレッドセーフなリスト操作

Pythonでリストをスレッドセーフに操作する最も基本的な方法は、ロックメカニズムを用いることです。

threading.Lockを用いた例


import threading

# スレッドセーフなリストを作成
my_list = []
lock = threading.Lock()

def add_to_list(value):
    with lock:  # ロックを取得
        my_list.append(value)  # リストに値を追加

# スレッドを作成して実行
threads = []
for i in range(10):
    thread = threading.Thread(target=add_to_list, args=(i,))
    threads.append(thread)
    thread.start()

# すべてのスレッドが終了するのを待つ
for thread in threads:
    thread.join()

print(my_list)

このコードでは、`threading.Lock`クラスを使用してリスト`my_list`へのアクセスを制御しています。`with lock:`という文は、そのブロック内でリストに対するアクセスがロックされ、他のスレッドからは触れないようにしています。

高度なテクニック

Queueモジュールを使った例

Pythonの`Queue`モジュールは、スレッドセーフなキューを提供します。このキューは内部でロックが取られているため、明示的にロックを取る必要はありません。


from queue import Queue
import threading

# スレッドセーフなキューを作成
q = Queue()

def add_to_queue(value):
    q.put(value)  # キューに値を追加

# スレッドを作成して実行
threads = []
for i in range(10):
    thread = threading.Thread(target=add_to_queue, args=(i,))
    threads.append(thread)
    thread.start()

# すべてのスレッドが終了するのを待つ
for thread in threads:
    thread.join()

# キューからリストに変換
my_list = list(q.queue)
print(my_list)

応用例

リスト内の要素を並行して処理

一般的に、リスト内の各要素に対して何らかの計算を行いたい場合があります。ここでは、リスト内の数値をそれぞれ二乗する簡単な例を考えます。


from concurrent.futures import ThreadPoolExecutor

# リスト内の数値を二乗する関数
def square_number(number):
    return number ** 2

numbers = [1, 2, 3, 4, 5]
squared_numbers = []

with ThreadPoolExecutor() as executor:
    squared_numbers = list(executor.map(square_number, numbers))

print(squared_numbers)

リストを複数の部分リストに分割


# リストをn個の部分リストに分割する関数
def split_list(lst, n):
    avg_len = len(lst) // n
    return [lst[i * avg_len: (i + 1) * avg_len] for i in range(n)]

numbers = list(range(100))
split_numbers = split_list(numbers, 4)
print(split_numbers)

条件に合う要素だけを並行してフィルター


# 条件に合う要素だけを残す関数
def filter_even(number):
    return number % 2 == 0

numbers = [1, 2, 3, 4, 5]


filtered_numbers = []

with ThreadPoolExecutor() as executor:
    filtered_numbers = list(filter(filter_even, numbers))

print(filtered_numbers)

まとめ

Pythonでリストをスレッドセーフに操作する技術は、多くの場面で役立つスキルです。基本的なロックメカニズムから、高度な`Queue`モジュール、さらには`ThreadPoolExecutor`を用いた並行処理まで、幅広い方法が存在します。これらのテクニックを理解し、適切に使用することで、より効率的なコードを書くことができるでしょう。

コメント

コメントする

目次