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

Posted

技术标签:

【中文标题】ProgrammingError: 在一个线程中创建的 SQLite 对象只能在同一个线程中使用【英文标题】:ProgrammingError: SQLite objects created in a thread can only be used in that same thread 【发布时间】:2018-06-21 10:22:19 【问题描述】:

我对编程很陌生。我以前尝试过 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

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

谢谢。

【问题讨论】:

不,它们显然是定义在它上面的 python 对象。错误消息涉及连接和光标。 【参考方案1】:

如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)

如您所见,数据被乱序插入,但仍然在while 循环中一一处理。

https://docs.python.org/3/library/queue.html https://docs.python.org/3/library/threading.html#threading.Thread.join https://docs.python.org/3/library/threading.html#threading.get_ident

【讨论】:

【参考方案2】:

我遇到了同样的问题,我通过在每次通话后关闭连接来解决它:

results = session.query(something, something).all()
session.close()

【讨论】:

【参考方案3】:

你可以试试这个:

engine=create_engine('sqlite:///data.db', echo=True, connect_args="check_same_thread": False)

它对我有用

【讨论】:

【参考方案4】:

您的光标“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"

【讨论】:

我遇到了这个。我正在学习一个似乎有一些 python2 东西的 udacity 全栈开发课程。所以这实际上是一个很好的学习机会。无论哪种方式都对我有用。我在 app.py 文件的顶部有以下内容,然后我从字面上复制并粘贴到该函数(路径)中,瞧,问题解决了。 python DBSession = sessionmaker(bind=engine) session = DBSession() 是否还需要con.close()with 来解决这个问题?【参考方案5】:
engine = create_engine(
'sqlite:///restaurantmenu.db',
connect_args='check_same_thread': False
)

为我工作

【讨论】:

嗨 J J,欢迎来到 Stack Overflow!请问你能改进这个答案吗?使用单反引号`来显示类似`code`的代码,并解释为什么有问题的代码回答了这个问题/完成了这个人问的工作.. 谢谢,将 args 添加到引擎对我来说效果很好。【参考方案6】:

在我的例子中,我有两个创建 sqlite 引擎的 python 文件,因此可能在不同的线程上运行。阅读 SQLAlchemy 文档here,似乎最好在两个文件中使用单例技术:

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

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

【讨论】:

【参考方案7】:

在您连接到数据库的位置添加以下内容。

conn = sqlite3.connect('your.db', check_same_thread=False)

【讨论】:

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

以上是关于ProgrammingError: 在一个线程中创建的 SQLite 对象只能在同一个线程中使用的主要内容,如果未能解决你的问题,请参考以下文章

Django ProgrammingError:在 Django 源代码中创建迁移后,关系已经存在?

psycopg2.ProgrammingError:无法在 odoo 14 中调整类型“NewId”

Python支持SQL数据库返回psycopg2.ProgrammingError:在尝试删除表中的数据时,关系不存在错误?

django.db.utils.ProgrammingError:表不存在

使用 psycopg2 在 python 中执行查询时出现“ProgrammingError:在或附近出现语法错误”

ProgrammingError:关系 django_session 的权限被拒绝