89 votes

Erreur de programmation : Les objets SQLite créés dans un thread ne peuvent être utilisés que dans ce même thread.

Je suis assez novice en matière de programmation. J'ai déjà essayé MySQL, mais c'est la première fois que j'utilise SQLite dans un site web python flask. Peut-être que j'utilise la syntaxe MySQL au lieu de SQLite, mais je n'arrive pas à trouver le problème.

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

Cela signifie-t-il que je ne peux pas utiliser le nom, l'adresse électronique et le mot de passe dans un fichier HTML ? Comment puis-je résoudre ce problème ?

Merci.

2 votes

Non, ce sont clairement des objets python définis juste au-dessus. Le message d'erreur parle de la connexion et du curseur.

3voto

Mohamed Abdalla Points 21

J'ai eu le même problème et je l'ai résolu en fermant ma connexion après chaque appel :

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

1voto

siikamiika Points 176

Comme mentionné dans https://docs.python.org/3/library/sqlite3.html et signalé par @Snidhi Sofpro dans un commentaire

Par défaut, check_same_thread est True et seul le thread de création peut utiliser la connexion. Si la valeur est False, la connexion renvoyée peut être partagée entre plusieurs threads. Lors de l'utilisation de plusieurs threads avec la même connexion, les opérations d'écriture doivent être sérialisées par l'utilisateur pour éviter la corruption des données.

Une façon de réaliser la sérialisation :

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)

Comme vous pouvez le constater, les données sont insérées dans le désordre, mais elles sont traitées une par une dans un fichier de type while boucle.

0voto

teddy waweru Points 1

L'erreur ne se situe pas sur les variables appelées dans votre .execute() mais plutôt les instances d'objets que SQLite utilise pour accéder à la base de données.
Je suppose que vous l'avez fait :

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

quelque part en haut du script de Flask, & ceci serait initialisé lorsque vous exécutez le script pour la première fois.
Lorsque le register est appelée, un nouveau thread, différent du script initial, gère le processus. Ainsi, dans ce nouveau thread, vous utilisez des instances d'objets qui proviennent d'un thread différent, ce que SQLite capture comme une erreur : à juste titre, car cela peut conduire à une corruption des données si vous prévoyez que votre BD soit accédée par différents threads pendant l'exécution de l'application.
Une méthode différente, au lieu de désactiver la fonctionnalité SQLite check-same-thread, vous pourriez essayer d'initialiser votre connexion à la base de données et votre curseur dans les méthodes HTTP qui sont appelées.
Avec cela, les objets SQLite et l'utilisation seront sur le même thread au moment de l'exécution.

Le code serait redondant, mais il pourrait vous sauver dans les situations où les données sont accédées de manière asynchrone, et il empêchera également la corruption des données.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X