J'ai un programme Python qui utilise le module "threading". Une fois par seconde, mon programme démarre un nouveau thread qui récupère des données sur le Web et stocke ces données sur mon disque dur. Je voudrais utiliser sqlite3 pour stocker ces résultats, mais je ne peux pas le faire fonctionner. Le problème semble concerner la ligne suivante:
conn = sqlite3.connect("mydatabase.db")
- Si je mets cette ligne de code dans chaque thread, j'obtiens un OperationalError m'indiquant que le fichier de base de données est verrouillé. Je suppose que cela signifie qu'un autre thread a ouvert mydatabase.db via une connexion sqlite3 et l'a verrouillé.
- Si je mets cette ligne de code dans le programme principal et que je passe l'objet de connexion (conn) à chaque thread, j'obtiens un ProgrammingError, disant que les objets SQLite créés dans un thread ne peuvent être utilisés que dans ce même thread.
Auparavant, je stockais tous mes résultats dans des fichiers CSV et je n'avais aucun de ces problèmes de verrouillage de fichier. Espérons que cela sera possible avec sqlite. Des idées?
Réponses:
Vous pouvez utiliser le modèle consommateur-producteur. Par exemple, vous pouvez créer une file d'attente partagée entre les threads. Le premier thread qui récupère les données du Web met ces données en file d'attente dans la file d'attente partagée. Un autre thread propriétaire d'une connexion à la base de données extrait les données de la file d'attente et les transmet à la base de données.
la source
Contrairement à la croyance populaire, les nouvelles versions de sqlite3 prennent en charge l'accès à partir de plusieurs threads.
Cela peut être activé via un argument de mot clé facultatif
check_same_thread
:sqlite.connect(":memory:", check_same_thread=False)
la source
Ce qui suit trouvé sur mail.python.org.pipermail.1239789
J'ai trouvé la solution. Je ne sais pas pourquoi la documentation python n'a pas un seul mot sur cette option. Nous devons donc ajouter un nouvel argument mot-clé à la fonction de connexion et nous pourrons en créer des curseurs dans un thread différent. Alors utilisez:
sqlite.connect(":memory:", check_same_thread = False)
fonctionne parfaitement pour moi. Bien sûr, à partir de maintenant, je dois m'occuper d'un accès multithreading sécurisé à la base de données. Quoi qu'il en soit merci à tous d'avoir essayé d'aider.
la source
check_same_thread
option: "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." Donc oui, vous pouvez utiliser SQLite avec plusieurs threads tant que votre code garantit qu'un seul thread peut écrire dans la base de données à un moment donné. Sinon, vous risquez de corrompre votre base de données.Passez au multitraitement . C'est beaucoup mieux, évolue bien, peut aller au-delà de l'utilisation de plusieurs cœurs en utilisant plusieurs processeurs, et l'interface est la même que l'utilisation du module de threading python.
Ou, comme Ali l'a suggéré, utilisez simplement le mécanisme de pool de threads de SQLAlchemy . Il gérera tout automatiquement pour vous et possède de nombreuses fonctionnalités supplémentaires, pour n'en citer que quelques-unes:
la source
Vous ne devriez pas du tout utiliser de threads pour cela. C'est une tâche triviale pour Twisted et qui vous mènerait probablement beaucoup plus loin de toute façon.
N'utilisez qu'un seul thread et que l'achèvement de la demande déclenche un événement pour effectuer l'écriture.
twisted s'occupera de la planification, des rappels, etc ... pour vous. Il vous remettra le résultat complet sous forme de chaîne, ou vous pouvez l'exécuter via un processeur de flux (j'ai une API Twitter et une API Friendfeed qui déclenchent les événements aux appelants lorsque les résultats sont toujours en cours de téléchargement).
En fonction de ce que vous faites avec vos données, vous pouvez simplement vider le résultat complet dans sqlite une fois terminé, le cuire et le vider, ou le cuire pendant sa lecture et le vider à la fin.
J'ai une application très simple qui fait quelque chose de proche de ce que vous voulez sur github. Je l'appelle pfetch ( extraction parallèle). Il saisit diverses pages selon un calendrier, diffuse les résultats dans un fichier et exécute éventuellement un script une fois chacune terminée. Il fait également des trucs fantaisistes comme les GET conditionnels, mais pourrait toujours être une bonne base pour tout ce que vous faites.
la source
Ou si vous êtes paresseux, comme moi, vous pouvez utiliser SQLAlchemy . Il gérera le threading pour vous (en utilisant le thread local et un pool de connexions ) et la façon dont il le fait est même configurable .
Pour un bonus supplémentaire, si / quand vous réalisez / décidez que l'utilisation de Sqlite pour une application simultanée va être un désastre, vous n'aurez pas à changer votre code pour utiliser MySQL, ou Postgres, ou quoi que ce soit d'autre. Vous pouvez simplement basculer.
la source
Vous devez utiliser
session.close()
après chaque transaction vers la base de données afin d'utiliser le même curseur dans le même thread sans utiliser le même curseur dans les multi-threads qui provoquent cette erreur.la source
Utilisez threading.Lock ()
la source
J'aime la réponse d'Evgeny - Les files d'attente sont généralement le meilleur moyen d'implémenter la communication inter-thread. Pour être complet, voici quelques autres options:
OperationalError
, mais l'ouverture et la fermeture de connexions comme celle-ci est généralement un non-non, en raison de la surcharge de performances.la source
Vous devez concevoir la simultanéité de votre programme. SQLite a des limitations claires et vous devez y obéir, voir la FAQ (également la question suivante).
la source
Scrapy semble être une réponse potentielle à ma question. Sa page d'accueil décrit ma tâche exacte. (Bien que je ne sois pas encore sûr de la stabilité du code.)
la source
Je jetterais un œil au module Y_serial Python pour la persistance des données: http://yserial.sourceforge.net
qui gère les problèmes de blocage entourant une seule base de données SQLite. Si la demande de concurrence devient lourde, on peut facilement configurer la classe Farm de nombreuses bases de données pour diffuser la charge sur le temps stochastique.
J'espère que cela aidera votre projet ... il devrait être assez simple à mettre en œuvre en 10 minutes.
la source
Je n'ai trouvé aucun point de repère dans aucune des réponses ci-dessus, alors j'ai écrit un test pour tout comparer.
J'ai essayé 3 approches
Les résultats et les points à retenir du benchmark sont les suivants
Vous pouvez trouver le code et la solution complète pour les benchmarks dans ma réponse SO ICI J'espère que cela aide!
la source
La raison la plus probable pour laquelle vous obtenez des erreurs avec des bases de données verrouillées est que vous devez émettre
conn.commit()
après avoir terminé une opération de base de données. Si vous ne le faites pas, votre base de données sera verrouillée en écriture et le restera. Les autres threads en attente d'écriture expireront après un certain temps (la valeur par défaut est définie sur 5 secondes, voir http://docs.python.org/2/library/sqlite3.html#sqlite3.connect pour plus de détails à ce sujet) .
Voici un exemple d'insertion correcte et concurrente:
import threading, sqlite3 class InsertionThread(threading.Thread): def __init__(self, number): super(InsertionThread, self).__init__() self.number = number def run(self): conn = sqlite3.connect('yourdb.db', timeout=5) conn.execute('CREATE TABLE IF NOT EXISTS threadcount (threadnum, count);') conn.commit() for i in range(1000): conn.execute("INSERT INTO threadcount VALUES (?, ?);", (self.number, i)) conn.commit() # create as many of these as you wish # but be careful to set the timeout value appropriately: thread switching in # python takes some time for i in range(2): t = InsertionThread(i) t.start()
Si vous aimez SQLite, ou avez d'autres outils qui fonctionnent avec les bases de données SQLite, ou souhaitez remplacer les fichiers CSV par des fichiers SQLite db, ou devez faire quelque chose de rare comme IPC inter-plateforme, alors SQLite est un excellent outil et très approprié à cet effet. Ne vous laissez pas contraindre à utiliser une solution différente si cela ne vous convient pas!
la source