sqlite 在线程中创建的对象只能在同一线程中使用

kuhbmx9i  于 2022-11-15  发布在  SQLite
关注(0)|答案(9)|浏览(213)

我找不到问题所在:

@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()

错误:
文件“C:\USERS\app.py”,第59行,寄存器c.Execute(“INSERT INTO USERS(NAME,EMAIL,USERNAME,PASSWORD)VALUES(?,?)”,(NAME,EMAIL,USERNAME,PASSWORD))ProgrammingError:在线程中创建的SQLite对象只能在同一线程中使用。对象是在线程ID 23508中创建的,这是线程ID 22640
这是否意味着我不能在一个HTML文件中使用姓名、电子邮件用户名和密码?我该怎么解决这个问题?

wswtfjt7

wswtfjt71#

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

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

70gysomp2#

您的光标‘c’不是在同一线程中创建的;它可能是在运行Flaskapp时初始化的。
您可能希望使用相同的方法生成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"
8zzbczxx

8zzbczxx3#

engine = create_engine(
'sqlite:///restaurantmenu.db',
connect_args={'check_same_thread': False}
)

对我很管用

9rbhqvlz

9rbhqvlz4#

您可以尝试以下操作:

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

这对我很管用

fhity93d

fhity93d5#

在我的例子中,我遇到了两个创建SQLite引擎的Python文件的相同问题,因此可能在不同的线程上操作。阅读SQLAlChemy文档here,似乎在两个文件中都使用单例技术更好:

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

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

rsaldnfx

rsaldnfx6#

正如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循环中一个接一个地处理。

sf6xfgos

sf6xfgos7#

我遇到了同样的问题,我在每次通话后都关闭了连接,从而修复了这个问题:

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

7vux5j2d8#

错误不在于.execute()中调用的变量,而在于SQLite用来访问数据库的对象示例。
我假设你有:

conn = sqlite3.connect('your_database.db')
c = conn.cursor()

在Flask脚本顶部的某个位置&这将在您第一次运行该脚本时进行初始化。
当调用register函数时,一个不同于初始脚本运行的新线程处理该进程。因此,在这个新线程中,您使用的是来自不同线程的对象示例,SQLite将其捕获为错误:这是合理的,因为如果您预期在应用程序运行期间不同的线程访问您的数据库,这可能会导致数据损坏。
因此,您可以尝试在被调用的HTTP方法中初始化DB连接&游标,而不是禁用Check-Same-Three SQLite功能。
这样,SQLite对象和利用率将在运行时位于同一线程上。
代码将是冗余的,但它可以在数据被异步访问的情况下节省您的时间,并且还可以防止数据损坏。

nbysray5

nbysray59#

创建“lobase.py”:

import sqlite3

def dbcon():
    return sqlite3.connect("your.db")

然后导入:

from database import dbcon

db = dbcon()

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

您可能不需要关闭它,因为线程将立即被终止。

相关问题