2011-06-24 4 views
1

J'ai ce scénario:Python multithreading

Une page Web créée avec Zope/Plone et une mine d'API python. Il y a une page Web, appelez-la "a", qui par une méthode python appelle une base de données (Postgres) et retourne des informations. Sur la page "a", vous pouvez modifier les données de base de données "offline" (j'entends que les modifications ne sont pas écrites dans la base de données instantanément mais dans un second moment quand vous appuyez sur "save" et appelez une API python). Alors, imaginez ce scénario: un utilisateur, appelé "Sam", charge la page et commence à modifier les données. Pendant ce temps, un utilisateur, appelé "Sara", modifie la base de données par la page "a" en cliquant sur "enregistrer". Maintenant, Sam n'a pas les données de la base de données: il va appuyer sur "sauvegarder" et écraser le changement de données de Sara.

Je voudrais avoir une alerte sur ma page en temps réel. Je pensais que je peux faire quelque chose comme ceci:

Faire un appel AJAX, qui n'est pas bloqué, et continuez avec le rendu de page. L'AJAX appelle une méthode python qui crée un thread qui fait une boucle infinie (sur une condition "X"). Lorsque j'écris des données sur la base de données, j'appelle une fonction qui va changer "X condition" en arrêtant le thread et en revenant à AJAX.

De plus, je ne peux pas verrouiller la base de données car je dois donner un accès libre à tous les utilisateurs qui veulent modifier ma base de données.

Mon problème est: comment puis-je identifier un thread python? Je viens de voir que toutes les méthodes d'une classe qui héritent de Thread veulent "self" comme paramètre. De plus, je dois appeler le thread en accédant à la page "a" et cela se trouvera quelque part dans le code (disons sur le "module des threads") mais les inserts sont sur l'autre module. Alors, comment puis-je réaliser mon idée?

Et si quelqu'un a une autre idée, me dire sans aucun problème :)

Répondre

3

Le royaume du problème que vous discutez est généralement appelé « Simultanéité ». Étant donné que votre méthode prévient ou empêche l'utilisateur de se mettre à jour lorsqu'un champ de l'élément cible change, l'approche est généralement appelée "Concurrence pessimiste". Une façon de procéder est de garder une trace de l'aspect de l'élément lors de sa sélection et de ne le mettre à jour que si la version de la base de données ressemble exactement à la version sélectionnée ou n'a pas été mise à jour depuis un certain temps (un champ d'horodatage peut être utile). Vous pouvez également essayer la concurrence optimiste, dans laquelle vous ne vérifiez que les champs qu'un utilisateur a mis à jour et qui sont sauvegardés dans le magasin de données n'ont pas été mis à jour par l'autre utilisateur. Ces deux méthodes sont les plus simples si vous choisissez une bibliothèque ORM qui prend en charge la concurrence.

Ma bibliothèque web python préférée est django, et voici une question sur SO à propos de la même situation que vous cherchez à résoudre: Django: How can I protect against concurrent modification of database entries. J'espère que ça aide.

La gestion de la simultanéité de la manière que vous suggérez est faisable mais devrait être évitée dans la plupart des situations. Je l'ai déjà fait en ajoutant de la concurrence à un grand système avec des objets complexes qui avaient des effets secondaires étendus et pas d'accès unifié aux données (il y avait environ 5 méthodes d'accès aux données sur la durée de vie du système). C'est une méthode complexe pour gérer les accès simultanés (je crois que j'avais une application cliente et que j'ai démarré un thread observateur après avoir coché les items "extraits" dans un tableau de données décrivant le type et l'identifiant de l'objet, , quand ils l'ont vérifié, et pour combien de temps cela était valable, au cas où le client qui a vérifié l'objet n'a pas réussi à le vérifier une fois terminé). Si vous n'utilisez pas de ORM et que vous affichez un message à l'utilisateur lorsque des modifications ont été apportées à l'élément, essayez de désactiver la dernière colonne d'horodatage mise à jour et de vérifier l'appel ajax pour voir si la dernière mise à jour est supérieur à ce qu'il était lorsque vous avez chargé l'article pour la première fois. Donc, si vous étiez en train de coder un moyen générique de le faire, vous auriez simplement besoin du nom de la table, de la clé primaire et de l'horodatage.

méthode webservice pourrait ressembler à:

def is_most_current(table_name, id): 
    db = MySQLdb.connect(passwd="moonpie",db="thangs") 
    c=db.cursor() 
    c.execute("SELECT last_updated from %s where id = %s", (table_name, id)) 
    return c.fetchone() 

En ce qui concerne les bibliothèques multithreading python, fils de python sont source de confusion et de produire de mauvaises performances grâce à des problèmes avec verrouillage global de python, vous voudrez peut-être en fait de lancer un nouveau processus de nombreux cas (la bibliothèque multi-traitement est assez équivalente et fonctionne mieux dans les scénarios de traitement en parallèle). En ce qui concerne le "moi", c'est une convention pythonique pour la référence à l'instance de la classe avec laquelle vous traitez, un peu comme "ceci" dans les langues C. Vous pouvez facilement identifier un thread en lui donnant un nom unique lorsque vous le construisez. Voir les documents multiprocessing ou threading pour plus d'informations. Si vous pouvez éviter le threading pour ce problème, je vous recommande de le faire.

+0

Votre réponse est bonne mais je veux remarquer que dans un certain sens, quand il a la page "a" ouverte, un autre utilisateur met à jour la base de données. Puis changez les champs d'une manière propoer (sur la "une" page) – DonCallisto

+0

Mis à jour ma réponse, tapait encore les derniers paragraphes quand j'ai eu votre commentaire. – marr75