【问题标题】:ProgrammingError: SQLite objects created in a thread can only be used in that same threadProgrammingError: 在一个线程中创建的 SQLite 对象只能在同一个线程中使用
【发布时间】: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 对象。错误消息涉及连接和光标。

标签: python mysql sqlite flask


【解决方案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 循环中一一处理。

【讨论】:

    【解决方案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 你能扩展一下吗?有哪些事情不能做?我不确定我是否完全理解
          • 只要在给定时间内只有一个线程通过连接写入,就可以安全使用。
          猜你喜欢
          • 2021-01-16
          • 2017-03-01
          • 2019-10-30
          • 2020-09-12
          • 2021-09-20
          • 2019-10-21
          • 1970-01-01
          • 2016-03-04
          • 2021-06-10
          相关资源
          最近更新 更多