2016-12-18 13 views
0

SQLiteは3.7.13以降マルチスレッドアクセスをサポートしており、Pythonのsqlite3モジュールは3.4以降サポートしているようです。Pythonでsqliteデータベースにアクセスする複数のスレッドをどのように処理すればよいですか?

import sqlite3 
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared" 
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False) 
with connection: 
    cursor = conneciton.cursor() 
    cursor.execute("SQL") 

これは動作しますが、あなたは発見するでしょう最初のものを使用すると、データベースまたは他の別のスレッドが破損する可能性があり、あなたのデータへのアクセスをロックする必要があるということです。それを使用するには、このようなコードを書くかもしれません。それは次のようになります。一つのスレッドがロックを取得した場合

import threading 
import sqlite3 
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared" 
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False) 
# NOTE: You'll need to share this same lock object with every other thread using the database 
lock = threading.Lock() 
with lock: 
    with connection: 
     cursor = connection.cursor() 
     cursor.execute("SQL") 
     connection.commit() 

今、別のスレッドが最初のスレッドが閉じるまで、それを取得し、限りすべてのスレッドが同じlockオブジェクトを使用するとし、前with lock:に思い出すことができません彼らwith connection:、あなたのデータは壊れないでしょう。

しかし、今では、接続でロックを回す方法が必要です。あなたは、カスタムクラスと、おそらく別の引数、またはでこれを行うことができます:

import threading 
import sqlite3 

class LockableSqliteConnection(object): 
    def __init__(self, dburi): 
     self.lock = threading.Lock() 
     self.connection = sqlite3.connect(dburi, uri=True, check_same_thread=False) 

dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared" 
lsc = LockableSqliteConnection(dburi) 
with lsc.lock: 
    with lsc.connection: 
     cursor = lsc.connection.cursor() 
     cursor.execute("SQL") 
     lsc.connection.commit() 

クラスの名前は私が持っているものを思い出させるので、これは、かなり良いですので、少なくとも私は忘れそうにありませんよ私のデータをロックして破損します。しかし、2つのwith声明を捨てる方法はありますか?とにかくロックなしで接続を使用すべきではないので、私は理想的にはそれらを単一のwithに組み合わせたいと思っています。

答えて

0

私は、この記事の助けを借りて、私のニーズを満たしている何かを書くことができました:http://effbot.org/zone/python-with-statement.htm

私のコードは次のようになります。このクラスの

import threading 
import sqlite3 

class LockableSqliteConnection(object): 
    def __init__(self, dburi): 
     self.lock = threading.Lock() 
     self.connection = sqlite3.connect(dburi, uri=True, check_same_thread=False) 
     self.cursor = None 
    def __enter__(self): 
     self.lock.acquire() 
     self.cursor = self.connection.cursor() 
     return self 
    def __exit__(self, type, value, traceback): 
     self.lock.release() 
     self.connection.commit() 
     if self.cursor is not None: 
      self.cursor.close() 
      self.cursor = None 

オブジェクトはwithで今直接使用可能ですステートメント:

また、便利なことに私のカーソルを開き、自動的にdaに加えた変更をコミットします。つまり、私の新しいクラスを呼び出すコードは短くなります。しかし、データベースの変更を自動的にコミットすることは、実際には最良のアイデアではないかもしれません...トランザクションを開始して途中でロールバックしたいという複雑なシナリオでは、その機能が問題を引き起こす可能性があります。私のSQLのニーズは非常にシンプルですが、私はロールバックすることはありませんので、今は私のプロジェクトに残しました。

+0

コードを試してみて、うまくいくようです。完全にテストしていませんが、問題が1つ見つかりました。 self.lock.release()は "_ _exit_ _"関数の最後の行でなければなりません。そうしないと、新しい接続が行われている間にロックが解除され、新しい接続のカーソルが「なし」に設定されます。 –

関連する問題