ProgrammingError:在线程中创建的SQLite对象只能在同一个线程中使用

Tan*_*nia 33 python mysql sqlite flask

我对编程很新.我之前尝试过MySQL,但现在我第一次在python烧瓶网站上使用SQLite了.所以也许我使用MySQL语法而不是SQLite,但我似乎无法找到问题.

Piece of my code: 

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm(request.form)
    if request.method=='POST' and form.validate():
        name =  form.name.data 
        email = form.email.data
        username = form.username.data
        password = sha256_crypt.encrypt(str(form.password.data))

        c.execute("INSERT INTO users(name,email,username,password) 
        VALUES(?,?,?,?)", (name, email, username, password))

        conn.commit

        conn.close()

The error:
 File "C:\Users\app.py", line 59, in register c.execute("INSERT INTO users(name,email,username,password) VALUES(?,?,?,?)", (name, email, username, password))
 ProgrammingError: SQLite objects created in a thread can only be used in that 
 same thread.The object was created in thread id 23508 and this is thread id 
 22640
Run Code Online (Sandbox Code Playgroud)

这是否意味着我不能在HTML文件中使用名称,电子邮件用户名和密码?我该如何解决这个问题?

谢谢.

小智 41

在与数据库建立连接的位置添加以下内容.

conn = sqlite3.connect('your.db', check_same_thread=False)
Run Code Online (Sandbox Code Playgroud)

  • 这样安全吗? (14认同)
  • 这个主题的未来读者的一些额外信息.按https://docs.python.org/3/library/sqlite3.html:默认情况下,check_same_thread为True,只有创建的线程可以使用该连接.如果设置为False,则可以跨多个线程共享返回的连接.当使用具有相同连接的多个线程时,应由用户序列化写操作以避免数据损坏. (10认同)
  • @merlin2011 你能详细说明一下吗?哪些事情是不应该做的?我不确定我是否完全理解 (4认同)
  • @uzu,我不明白为什么不这样做,只要您自己进行同步以确保只有一个线程同时使用该对象。 (3认同)
  • 只要在给定时间内只有单个线程通过连接进行写入,就可以安全使用。 (2认同)

ndr*_*rix 22

你的光标'c'不是在同一个线程中创建的; 它可能是在运行Flask应用程序时初始化的.

您可能希望以相同的方法生成SQLite对象(连接和光标),例如:

  @app.route('/')
  def dostuff():
    with sql.connect("database.db") as con:
      name = "bob"
      cur = con.cursor()
      cur.execute("INSERT INTO students (name) VALUES (?)",(name))
      con.commit()
      msg = "Done"
Run Code Online (Sandbox Code Playgroud)

  • 是否还需要 `con.close()` 或者 `with` 来处理这个问题? (6认同)
  • 但是在每个请求上重新连接到数据库是否会不必要地昂贵? (6认同)
  • 我遇到了这个。我正在通过一个似乎有一些python2东西的udacity全栈开发课程。因此,实际上这是一个很好的学习机会。无论哪种方式对我都有效。我在app.py文件的顶部放置了以下内容,然后从字面上复制并粘贴到了该函数(路径)中,瞧,问题解决了。Python DBSession = sessionmaker(bind = engine)会话= DBSession() (2认同)

小智 10

engine = create_engine(
'sqlite:///restaurantmenu.db',
connect_args={'check_same_thread': False}
)
Run Code Online (Sandbox Code Playgroud)

为我工作

  • 嗨,JJ,欢迎来到Stack Overflow!请您改善这个答案?使用单反引号\\来显示这样的\\ code \`代码,并解释为什么所讨论的代码回答了问题/完成了所要求的工作。 (3认同)

小智 8

你可以试试这个:

engine=create_engine('sqlite:///data.db', echo=True, connect_args={"check_same_thread": False})
Run Code Online (Sandbox Code Playgroud)

这对我有用


ng1*_*g10 6

就我而言,我在创建 sqlite 引擎的两个 python 文件中遇到了同样的问题,因此可能在不同的线程上运行。在此处阅读 SQLAlchemy 文档,似乎在两个文件中都使用单例技术更好:

# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool
engine = create_engine('sqlite:///mydb.db',
                poolclass=SingletonThreadPool)
Run Code Online (Sandbox Code Playgroud)

它不能解决所有情况,这意味着我偶尔会遇到相同的错误,但我可以轻松克服它,刷新浏览器页面。因为我只用它来调试我的代码,所以这对我来说没问题。对于更永久的解决方案,应该选择另一个数据库,如 PostgreSQL 或其他数据库


sii*_*ika 5

正如https://docs.python.org/3/library/sqlite3.html中提到的,并由 @Snidhi Sofpro 在评论中指出

默认情况下,check_same_thread 为 True,并且只有创建线程可以使用该连接。如果设置为 False,则返回的连接可以在多个线程之间共享。当使用具有相同连接的多个线程时,用户应将写入操作序列化,以避免数据损坏。

实现序列化的一种方法:

import threading
import sqlite3
import queue
import traceback
import time
import random

work_queue = queue.Queue()

def sqlite_worker():
    con = sqlite3.connect(':memory:', check_same_thread=False)
    cur = con.cursor()
    cur.execute('''
        CREATE TABLE IF NOT EXISTS test (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            text TEXT,
            source INTEGER,
            seq INTEGER
        )
    ''')
    while True:
        try:
            (sql, params), result_queue = work_queue.get()
            res = cur.execute(sql, params)
            con.commit()
            result_queue.put(res)
        except Exception as e:
            traceback.print_exc()

threading.Thread(target=sqlite_worker, daemon=True).start()

def execute_in_worker(sql, params):
    # you might not really need the results if you only use this
    # for writing unless you use something like https://www.sqlite.org/lang_returning.html
    result_queue = queue.Queue()
    work_queue.put(((sql, params), result_queue))
    return result_queue.get(timeout=5)

def insert_test_data(seq):
    time.sleep(random.randint(0, 100) / 100)
    execute_in_worker(
        'INSERT INTO test (text, source, seq) VALUES (?, ?, ?)',
        ['foo', threading.get_ident(), seq]
    )

threads = []
for i in range(10):
    thread = threading.Thread(target=insert_test_data, args=(i,))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

for res in execute_in_worker('SELECT * FROM test', []):
    print(res)

# (1, 'foo', 139949462500928, 9)
# (2, 'foo', 139949496071744, 5)
# (3, 'foo', 139949479286336, 7)
# (4, 'foo', 139949487679040, 6)
# (5, 'foo', 139949854099008, 3)
# (6, 'foo', 139949470893632, 8)
# (7, 'foo', 139949862491712, 2)
# (8, 'foo', 139949845706304, 4)
# (9, 'foo', 139949879277120, 0)
# (10, 'foo', 139949870884416, 1)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,数据是乱序插入的,但仍然是在循环中一一处理while