1
1

More than 1 year has passed since last update.

Python マルチスレッドで完全にデッドロックなしでSQLite3を使うためのメモ

Last updated at Posted at 2022-02-23

はじめに

トランザクションやらコミットのこと無知な状態でマルチスレッドでsqliteやろうとしたらデッドロックしたのでメモ

threading.Event()でトランザクション前にスレッドを待機させる

import threading
import random
import time

# event3の中でevent1→event2の順でイベントが実行される
# event1はランダム秒後に開始する、event3の終了を待つ,終了したらevent3_endをclear
# event2はevent1が開始するまで待機状態
# event3はevent1が開始したらevent1をclearにしevent2を待つ
# event3でevent2が開始したらevent2をclearにしevent3の終了を告げる
# 上記繰り返し

test_event1_begin = threading.Event()
test_event2_begin = threading.Event()
test_event3_end = threading.Event()


def test_sql1():
    global test_event3_end
    while True:
        interval = random.uniform(1, 3)
        print(f'evet1 {interval}sec 後に開始')
        time.sleep(interval)
        # event1を開始
        test_event1_begin.set()
        # event3が終了するまで待機
        test_event3_end.wait()
        test_event3_end.clear()


def test_sql2():
    global test_event1_end, test_event2_end
    while True:
        time.sleep(0.001)
        # event1を待機
        test_event1_begin.wait()
        interval = random.uniform(1, 3)
        print(f'evet2 {interval}sec 後に開始', end='\n')
        time.sleep(interval)
        # event2を開始
        test_event2_begin.set()


def test_sql3():
    global test_event1_end, test_event2_end, test_event3_end
    while True:
        time.sleep(0.001)
        test_event1_begin.wait()
        test_event1_begin.clear()
        test_event2_begin.wait()
        test_event2_begin.clear()
        print('event3終了')
        time.sleep(1)
        test_event3_end.set()


def main():
    thread_list = []
    tasks = [test_sql1, test_sql2, test_sql3]
    for task in tasks:
        th = threading.Thread(target=task, args=())
        th.start()
        thread_list.append(th)
    for th in thread_list:
        th.join()


if __name__ == '__main__':
    main()

evet1 1.198280231761349sec 後に開始
evet2 1.606435444539516sec 後に開始
event3終了
evet1 2.7041704830942663sec 後に開始
evet2 1.2936549765944632sec 後に開始
event3終了
evet1 1.3772512376025186sec 後に開始
evet2 1.7498730389596326sec 後に開始
event3終了

結論

上記のように別スレッドからEventフラグを使ってスレッド管理するのもありですが、トランザクション処理は単一のスレッドで行ったほうが管理しやすいかなと思いました。パフォーマンスもこっちのほうがいいのでは?

# 以下、DBに反映させたいデータ
q1 = []
q2 = []
q3 = []

def th1():
    while True:
        # q1キューにデータを入れる処理
        q1.append('any')

def th2():
    while True:
        # q2キューにデータを入れる処理
        q2.append('any')

def th3():
    while True:
        # q3キューにデータを入れる処理
        q3.append('any')

def any_transaction():
    global q1,q2,q3
    while True:
        time.sleep(0.001)
        # キューにデータがあれば順次する
        if q1:
            # q1をdbに入れる処理
            pass
        if q1:
            # q1をdbに入れる処理
            pass
        if q1:
            # q1をdbに入れる処理
            pass
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1