使用MySQLdb時,如果創建一個連接connection,多個線程同時使用,會不會有問題?
在下文中,我們將模擬這種場景,看是否會出現問題。
1.示例
1.1 正常的情況
創建一個連接,兩個線程同時使用這個連接,生成游標cursor,進行查詢,並輸出結果。
程序啟動后,讓線程1睡眠1s,保證讓線程2線執行。
import MySQLdb
import threading
import time
def get_connection():
host = "127.0.0.1"
port = 3306
user = "root"
passwd = "Aa123456"
conn = MySQLdb.connect(host=host, port=port, user=user,passwd=passwd, connect_timeout=2, charset="utf8")
return conn
def thread1_runtime(conn, sql):
time.sleep(1)
cursor = conn.cursor()
cursor.execute(sql)
ret = cursor.fetchone()
thread_name = threading.current_thread().name
print("thread name:%s, ret:%s" %(thread_name, ret))
def thread2_runtime(conn, sql):
cursor = conn.cursor()
cursor.execute(sql)
ret = cursor.fetchone()
thread_name = threading.current_thread().name
print("thread name:%s, ret:%s" %(thread_name, ret))
if __name__ == "__main__":
thread1_sql = "select 1"
thread2_sql = "select 2"
conn = get_connection()
thread1 = threading.Thread(target=thread1_runtime, name="thread 001", args=(conn, thread1_sql))
thread2 = threading.Thread(target=thread2_runtime, name="thread 002", args=(conn, thread2_sql))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("...main exit....")
從結果可以看到,一切正常。
output:
thread name:thread 002, ret:(2,)
thread name:thread 001, ret:(1,)
...main exit....
1.2 異常的情況
以下例子中,程序啟動后,線程1和線程2使用同一連接創建新的游標cursor,執行查詢。
接着,線程1睡眠1秒,線程2睡眠2秒。
最后各自打印查詢結果。
import MySQLdb
import threading
import time
def get_connection():
host = "127.0.0.1"
port = 3306
user = "root"
passwd = "Aa123456"
conn = MySQLdb.connect(host=host, port=port, user=user,passwd=passwd, connect_timeout=2, charset="utf8")
return conn
def thread1_runtime(conn, sql):
cursor = conn.cursor()
cursor.execute(sql)
time.sleep(1)
ret = cursor.fetchone()
thread_name = threading.current_thread().name
print("thread name:%s, ret:%s" %(thread_name, ret))
def thread2_runtime(conn, sql):
cursor = conn.cursor()
cursor.execute(sql)
time.sleep(2)
ret = cursor.fetchone()
thread_name = threading.current_thread().name
print("thread name:%s, ret:%s" %(thread_name, ret))
if __name__ == "__main__":
thread1_sql = "select 1"
thread2_sql = "select 2"
conn = get_connection()
thread1 = threading.Thread(target=thread1_runtime, name="thread 001", args=(conn, thread1_sql))
thread2 = threading.Thread(target=thread2_runtime, name="thread 002", args=(conn, thread2_sql))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("...main exit....")
output:
Exception in thread thread 002:
Traceback (most recent call last):
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "one_conn_mutiple_cursor.py", line 28, in thread2_runtime
cursor.execute(sql)
File "/Users/lanyang/workspace/orange-service/.venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 198, in execute
res = self._query(query)
File "/Users/lanyang/workspace/orange-service/.venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 304, in _query
db.query(q)
File "/Users/lanyang/workspace/orange-service/.venv/lib/python3.6/site-packages/MySQLdb/connections.py", line 217, in query
_mysql.connection.query(self, query)
MySQLdb._exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now")
thread name:thread 001, ret:(1,)
...main exit....
從打印結果可以看到,線程1可以正常打印結果,但線程2報錯了。
線程1睡眠先結束,去獲取查詢結果,可以打印。
線程2睡眠后結束,再去獲取查詢結果,已經無法獲取結果了。
2.總結
從上面例子中,我們可以得出結論:
一個連接connection同一個時間點只能有一個cursor,執行sql,並獲取結果。
所以,不要在多個線程中同時使用一個連接connection,否則會出現不可預料的結果。