Je développe des systèmes simultanés depuis plusieurs années maintenant, et je connais assez bien le sujet malgré mon manque de formation formelle (c’est-à-dire qu’il n’ya pas de diplôme). Il y a quelques nouveaux langages qui sont devenus populaires pour au moins parler de ces derniers temps et qui sont conçus pour faciliter la concurrence, tels que Erlang et Go. Il semble que leur approche de la simultanéité fasse écho à ma propre expérience quant à la manière de rendre des systèmes évolutifs et de tirer parti de plusieurs cœurs / processeurs / machines.
Cependant, je trouve qu'il existe très peu d'outils pour vous aider à visualiser ce que vous avez l'intention de faire et à vérifier que vous êtes au moins près de votre vision d'origine. Déboguer du code concurrent peut être un cauchemar avec des langages qui ne sont pas conçus pour la concurrence (comme C / C ++, C #, Java, etc.). En particulier, il est presque impossible de recréer les conditions qui se produisent facilement sur un système dans votre environnement de développement.
Alors, quelles sont vos approches pour concevoir un système prenant en charge le traitement simultané et simultané? Exemples:
- Comment déterminez-vous ce qui peut être rendu simultané par rapport à ce qui doit être séquentiel?
- Comment reproduire des conditions d'erreur et voir ce qui se passe pendant l'exécution de l'application?
- Comment visualisez-vous les interactions entre les différentes parties concourantes de l'application?
J'ai mes propres réponses à certaines de ces questions, mais j'aimerais aussi en apprendre un peu plus.
modifier
Jusqu'à présent, nous avons beaucoup de bonnes contributions. Beaucoup d'articles liés à sont très bons et j'en ai déjà lu quelques-uns.
Mon expérience personnelle avec la programmation concurrente m'amène à penser que vous avez besoin d'un état d'esprit différent de celui de la programmation séquentielle. La fracture mentale est probablement aussi large que la différence entre la programmation orientée objet et la programmation procédurale. J'aimerais que cette série de questions se concentre davantage sur les processus de pensée nécessaires (à savoir la théorie) pour aborder systématiquement les réponses. Lorsque vous fournissez des réponses plus concrètes, il est utile de donner un exemple - quelque chose que vous avez vécu personnellement.
But pour le Bounty
Ne me dis pas ce que je devrais faire. J'ai déjà cela sous contrôle. Dites-moi ce que vous faites. Dis-moi comment tu résous ces problèmes.
la source
Réponses:
Beaucoup des meilleurs programmeurs que je connaisse n'ont pas terminé l'université. Quant à moi j'ai étudié la philosophie.
Oui
nous commençons généralement par une métaphore de 1 000 km pour clarifier notre architecture pour nous-mêmes (d’une part) et pour les autres (d’autre part).
Face à ce problème, nous avons toujours trouvé le moyen de limiter la visibilité des objets concurrents à celle d'objets non concurrents.
Dernièrement, j'ai découvert Actors dans Scala et j'ai vu que mes anciennes solutions étaient une sorte de "miniacteurs", beaucoup moins puissants que ceux de Scala. Donc, ma suggestion est de partir de là.
Une autre suggestion consiste à éviter autant de problèmes que possible: nous utilisons par exemple un cache centralisé (terre cuite) au lieu de conserver les cartes en mémoire, des rappels de classe interne plutôt que des méthodes synchronisées, l'envoi de messages plutôt que l'écriture de mémoire partagée, etc.
De toute façon, avec Scala, c'est beaucoup plus facile.
Pas de vraie réponse ici. Nous avons quelques tests unitaires pour la simultanéité et une suite de tests de charge pour insister autant que possible sur l'application.
Encore une fois pas de vraie réponse: nous concevons notre métaphore sur le tableau blanc et nous essayons de nous assurer qu'il n'y a pas de conflits sur le plan architectural.
Pour Arch ici, je veux dire la définition de Neal Ford: Sw Architecture est tout ce qui sera très difficile à changer plus tard.
Peut-être, mais pour moi, il est tout simplement impossible de penser de manière parallèle, il est donc préférable de concevoir notre logiciel de manière à ne pas avoir à penser en parallèle et à utiliser des garde-corps clairs pour éviter les collisions entre les voies d'accès simultané.
la source
Pour moi, tout est une question de données. Brisez vos données correctement et le traitement en parallèle est facile. Tous les problèmes de rétention, de blocages, et ainsi de suite.
Je sais que ce n'est pas le seul moyen de paralléliser, mais pour moi, c'est de loin le plus utile.
Pour illustrer une histoire (pas si rapide):
J'ai travaillé sur un grand système financier (contrôle boursier) de 2007 à 2009 et le volume de traitement des données était très important. À titre d’illustration, tous les calculs effectués sur un seul compte d’un client prennent environ 1 à 3 secondes sur leur poste de travail moyen, et il existe plus de 30 000 comptes. Chaque nuit, la fermeture du système était très pénible pour les utilisateurs (généralement plus de 6 heures de traitement, sans aucune marge d'erreur pour eux).
En étudiant davantage le problème, nous avons découvert que nous pouvions paralyser les calculs sur plusieurs ordinateurs, mais que nous aurions toujours un goulot d'étranglement sur l'ancien serveur de base de données (un serveur SQL 2000 émulant SQL 6.5).
Il était assez clair que notre paquet de traitement minimum était le calcul d'un compte unique et que le principal goulot d'étranglement était la rétention du serveur de base de données (nous pouvions voir sur le "sp_who" plusieurs connexions en attente d'effectuer le même traitement). Ainsi, le processus parallèle s'est déroulé comme suit:
1) Un seul producteur, chargé de lire la base de données ou d’écrire dessus, de manière séquentielle. Aucune concurrence autorisée ici. Le producteur a préparé une file d’emplois pour les consommateurs. La base de données appartenait uniquement à ce producteur.
2) Plusieurs consommateurs, sur plusieurs machines. Chacun des consommateurs a reçu un paquet complet de données, prêt de la file d'attente, à calculer. Chaque opération de dé-file est synchronisée.
3) Après le calcul, chaque consommateur a renvoyé au producteur les données dans une file d'attente synchronisée en mémoire afin de les conserver.
Il y avait plusieurs points de contrôle, plusieurs mécanismes pour assurer que les transactions étaient correctement sauvegardées (aucun n'était laissé derrière), mais tout le travail en valait la peine. En fin de compte, les calculs répartis sur 10 ordinateurs (plus l'ordinateur du producteur / de la file d'attente) ont réduit le temps de fermeture de l'ensemble du système à 15 minutes.
Supprimer les problèmes de rétention causés par la mauvaise gestion des accès simultanés SQL 6.5 nous avait donné un gros avantage. Le reste était à peu près linéaire, chaque nouvel ordinateur ajouté à la "grille" diminuant le temps de traitement, jusqu'à atteindre "l'efficacité maximale" des opérations de lecture / écriture séquentielles sur la base de données.
la source
Travailler dans un environnement multi-threading est difficile et nécessite une discipline de codage. Vous devez suivre les directives appropriées pour verrouiller, relâcher le verrou, accéder aux variables globales, etc.
Permettez-moi d'essayer de répondre à votre question un par un
Utiliser la concurrence pour
1) Polling: - il faut un thread pour interroger quelque chose en permanence ou envoyer la mise à jour régulièrement. (Des concepts tels que des cœur, qui envoient des données sur un intervalle régulier au serveur central pour dire que je suis en vie.)
2) Les opérations qui ont des entrées / sorties lourdes pourraient être effectuées en parallèle. Le meilleur exemple est enregistreur. Le thread de journalisation pourrait être un thread séparé.
3) Tâches similaires sur des données différentes. Si une tâche se produit sur des données différentes mais de nature très similaire, différents threads peuvent le faire. Le meilleur exemple sera les requêtes du serveur.
Et bien sûr, beaucoup d’autres comme celui-ci dépendent de l’application.
L'utilisation des journaux et des impressions de débogage dans les journaux. Essayez de vous connecter également l'identifiant du thread afin que vous puissiez voir ce qui se passe dans chaque thread.
Une façon de générer une condition d'erreur consiste à placer le délai délibéré (dans le code de débogage) aux endroits où le problème se produit, et à arrêter énergiquement ce thread. Des opérations similaires peuvent également être effectuées dans les débogueurs, mais je ne l’ai pas encore fait.
Placez les journaux dans vos serrures pour savoir qui verrouille quoi et quand et qui a essayé de le verrouiller. Comme je l'ai dit plus tôt, essayez de mettre l'identifiant de thread dans le journal pour comprendre ce qui se passe dans chaque thread.
C’est tout simplement mon conseil, qui consiste à travailler sur l’application multithread depuis environ 3 ans, et espère que cela aidera.
la source
Je me demanderais d’abord si l’application (ou le composant) bénéficiera réellement d’un traitement simultané, ou en termes simples, où se situe le goulot d’étranglement? Évidemment, la concurrence n’apportera pas toujours un avantage pour l’investissement nécessaire à son bon fonctionnement. Si cela ressemble à un candidat, je travaillerais de manière ascendante - en essayant de trouver la plus grande opération ou l'ensemble des opérations pouvant faire son travail efficacement de manière isolée - je ne veux pas créer de fils pour des tâches insignifiantes et peu rentables. opérations - je cherche des acteurs .
En travaillant avec Erlang, je suis devenu un adepte absolu du concept d’utilisation du passage de messages asynchrone et du modèle d’acteur pour la simultanéité - c’est intuitif, efficace et clair.
Hors de Concurrency Comprendre Acteur
Le modèle de concurrence Erlang est plus facile à comprendre et à mettre au point que le verrouillage et le partage de données. La façon dont votre logique est isolée permet de tester facilement les composants en leur transmettant des messages.
Travailler avec des systèmes concurrents, c’est à peu près ainsi que ma conception fonctionnait de toute façon dans n’importe quel langage - une file d’attente à partir de laquelle plusieurs threads extraient des données, effectuent une opération simple, puis répètent ou repoussent dans la file d’attente. Erlang ne fait que renforcer les structures de données immuables pour prévenir les effets secondaires et réduire le coût et la complexité de la création de nouveaux threads.
Ce modèle n’est pas exclusif à Erlang, même dans le monde Java et .NET, il existe des moyens de le créer - j’examinerais le CCR (Concurrence and Coordination Runtime) et Relang (il existe également Jetlang pour Java).
D'après mon expérience, la seule chose que j'ai pu faire est de m'engager à tout tracer / enregistrer. Chaque processus / thread doit avoir un identifiant et chaque nouvelle unité de travail doit avoir un identifiant de corrélation. Vous devez être capable de parcourir vos journaux et de retracer exactement ce qui était en cours de traitement et à quel moment - je n’ai pas eu la magie de l’éliminer.
Voir ci-dessus, c'est moche mais ça marche. La seule autre chose que je fais est d’utiliser des diagrammes de séquence UML - bien sûr, c’est pendant la phase de conception - mais vous pouvez les utiliser pour vérifier que vos composants parlent comme vous le souhaitez.
la source
- Mes réponses sont spécifiques à MS / Visual Studio -
Cela va prendre connaissance du domaine, il ne va pas y avoir de déclaration générale pour couvrir cela.
Beaucoup de journalisation, être capable d'activer / désactiver / relancer la journalisation dans les applications de production afin de la capturer dans la production. VS2010 Intellitrace est censé pouvoir vous aider, mais je ne l'ai pas encore utilisé.
Je n'ai pas de bonne réponse à cela, j'aimerais bien en voir une.
la source
Je ne suis pas d'accord avec votre affirmation selon laquelle C n'est pas conçu pour la concurrence. C est conçu pour la programmation générale de systèmes et jouit d’une ténacité à indiquer les décisions critiques à prendre, et continuera de le faire pour les années à venir. Cela est vrai même lorsque la meilleure décision peut être de ne pas utiliser C. De plus, la concurrence d'accès en C est aussi difficile que votre conception est complexe.
J'essaie, dans la mesure de mes moyens, de mettre en oeuvre des verrous avec l'idée que, finalement, une programmation réellement pratique et sans verrous pourrait devenir une réalité pour moi. Par verrouillage, je ne parle pas d'exclusion mutuelle, je veux simplement dire d'un processus qui implémente une concurrence sécurisée sans recourir à l'arbitrage. Par pratique, je veux dire quelque chose qui est plus facile à porter qu’à implémenter. J'ai aussi très peu de formation formelle en informatique, mais je suppose que je suis autorisé à le souhaiter :)
Suite à cela, la plupart des insectes que je rencontre deviennent relativement peu profonds, ou si complètement ahurissants que je me retire dans un pub. Le pub devient une option attrayante uniquement lorsque le profilage d'un programme le ralentit suffisamment pour exposer des races supplémentaires qui ne sont pas liées à ce que j'essaie de trouver.
Comme d'autres l'ont souligné, le problème que vous décrivez est extrêmement spécifique à un domaine. J'essaie simplement, du mieux que je peux, d'éviter tout cas qui pourrait nécessiter un arbitrage (en dehors de mon processus) dans la mesure du possible. Si cela semble être une douleur royale, je réévalue la possibilité de donner à plusieurs threads ou processus un accès simultané et non sérialisé à quelque chose.
Encore une fois, jetez 'distribué' dedans et l'arbitrage devient un must. Avez-vous un exemple spécifique?
la source
Sur la base de mon expérience, la réponse à ces deux aspects est la suivante:
Traçage distribué
Le traçage distribué est une technologie qui capture les données de synchronisation de chaque composant simultané de votre système et vous les présente sous forme graphique. Les représentations des exécutions simultanées sont toujours entrelacées, ce qui vous permet de voir ce qui fonctionne en parallèle et ce qui ne l'est pas.
Le traçage distribué doit ses origines (bien sûr) aux systèmes distribués, qui sont par définition asynchrones et hautement concurrents. Un système distribué avec traçage distribué permet aux utilisateurs de:
a) identifier les goulots d'étranglement importants, b) obtenir une représentation visuelle des «exécutions» idéales de votre application, et c) fournir une visibilité sur le comportement simultané exécuté, d) obtenir des données temporelles pouvant être utilisées pour évaluer les différences entre les changements de votre système (extrêmement important si vous avez de forts accords de niveau de service).
Les conséquences du traçage distribué sont toutefois les suivantes:
Il ajoute une surcharge à tous vos processus simultanés, car il se traduit par davantage de code à exécuter et à soumettre éventuellement sur un réseau. Dans certains cas, cette surcharge est très importante - même Google utilise son système de traçage Dapper uniquement sur un petit sous-ensemble de toutes les demandes afin de ne pas gâcher l'expérience utilisateur.
Il existe de nombreux outils différents, qui ne sont pas tous interopérables les uns avec les autres. Ceci est quelque peu amélioré par des normes comme OpenTracing, mais pas complètement résolu.
Il ne vous dit rien sur les ressources partagées et leur statut actuel. Vous pourrez peut-être deviner, en fonction du code de l'application et de ce que le graphique que vous voyez est en train de vous montrer, mais ce n'est pas un outil utile à cet égard.
Les outils actuels supposent que vous avez de la mémoire et du stockage à revendre. L'hébergement d'un serveur timeseries peut ne pas être bon marché, en fonction de vos contraintes.
Logiciel de suivi des erreurs
Je me connecte à Sentry ci-dessus principalement parce que c'est l'outil le plus utilisé et pour une bonne raison - un logiciel de suivi des erreurs comme l'exécution du détournement de Sentry permet de transmettre simultanément une trace de pile des erreurs rencontrées à un serveur central.
L’avantage net de ce logiciel dédié en code concurrent:
Cela signifie que vous pouvez déterminer quel système simultané rencontre quel type d'erreur sans avoir à passer par d'innombrables rapports d'erreur simultanés. Si vous avez déjà été victime de spam par courrier électronique à partir d'un système distribué, vous savez à quoi ressemble un enfer.
Vous pouvez même "baliser" différents aspects de votre système concurrent (bien que cela suppose que votre travail ne soit pas entrelacé sur un seul thread, ce qui techniquement n'est pas simultané puisque le thread saute simplement d'une tâche à une autre, mais doit toujours traiter les gestionnaires d'événements à compléter) et voir une ventilation des erreurs par tag.
Ceci, en plus des traces de pile méticuleuses (et des mappes de sources, si vous devez fournir une version réduite de vos fichiers), facilite la détermination de ce qui ne va pas une grande partie du temps.
Les inconvénients d'un tel logiciel incluent:
Comme tout, ils ajoutent du volume. Vous ne voudrez peut-être pas un tel système sur du matériel embarqué, par exemple. Je recommande fortement de faire un essai de ce logiciel, en comparant une exécution simple avec et sans échantillonnage sur quelques centaines d'essais sur une machine inactive.
Toutes les langues ne sont pas prises en charge de la même manière, car bon nombre de ces systèmes reposent sur la capture implicite d'une exception et toutes les langues ne disposent pas d'exceptions robustes. Cela étant dit, il existe des clients pour de nombreux systèmes.
Ils peuvent constituer un risque pour la sécurité, car bon nombre de ces systèmes sont essentiellement à source fermée. Dans ce cas, faites preuve de la diligence requise pour les rechercher ou, si vous préférez, lancez les vôtres.
Ils pourraient ne pas toujours vous donner les informations dont vous avez besoin. C'est un risque avec toutes les tentatives pour ajouter de la visibilité.
La plupart de ces services ont été conçus pour des applications Web hautement concurrentes. Tous les outils ne sont donc peut-être pas parfaitement adaptés à votre cas d'utilisation.
En résumé : la visibilité est la partie la plus cruciale de tout système concurrent. Les deux méthodes que je décris ci-dessus, associées à des tableaux de bord dédiés sur le matériel et les données pour obtenir une image globale du système à tout moment, sont largement utilisées dans l’industrie, précisément pour remédier à ce problème.
Quelques suggestions supplémentaires
J'ai passé plus de temps que prévu à réparer des codes chez des personnes qui ont essayé de résoudre des problèmes concurrents de manière terrible. À chaque fois, j'ai trouvé des cas où les choses suivantes pourraient grandement améliorer l'expérience des développeurs (ce qui est tout aussi important que l'expérience des utilisateurs):
Comptez sur les types . La saisie permet de valider votre code et peut être utilisée au moment de l'exécution en tant que protection supplémentaire. Là où la saisie n'existe pas, utilisez des assertions et un gestionnaire d’erreur approprié pour intercepter les erreurs. Le code simultané nécessite un code défensif, et les types constituent le meilleur type de validation disponible.
Un bon test de liaison vérifie si, lorsqu'un composant parle à un autre composant de manière isolée , le message reçu et le message envoyé sont les mêmes que ceux attendus. Si vous avez deux composants ou plus qui dépendent d'un service partagé pour communiquer, mettez-les en synergie, demandez-leur d'échanger des messages via le service central et voyez s'ils obtiennent tous ce que vous attendez.
Découper des tests impliquant beaucoup de composants en un test des composants eux-mêmes et un test de la communication de chacun des composants vous donne une confiance accrue dans la validité de votre code. Un ensemble de tests aussi rigoureux vous permet de faire respecter les contrats entre les services et de détecter les erreurs inattendues qui se produisent lorsqu'ils sont exécutés simultanément.
Certains de ces outils sont livrés avec des langages - Rust, par exemple, garantit que votre code n’aura pas de situation de concurrence critique au moment de la compilation, tandis que Go propose un détecteur de blocage intégré qui fonctionne également au moment de la compilation. Si vous pouvez résoudre les problèmes avant qu'ils n'atteignent la production, c'est toujours une victoire.
Une règle générale: conception en cas d'échec dans les systèmes concurrents . Anticipez que les services communs vont planter ou casser. Cela vaut même pour le code qui n'est pas distribué sur plusieurs ordinateurs: le code simultané sur un seul ordinateur peut s'appuyer sur des dépendances externes (telles qu'un fichier journal partagé, un serveur Redis, un putain de serveur MySQL) qui peuvent disparaître ou être supprimées à tout moment. .
Pour ce faire, la meilleure solution consiste à valider de temps à autre l'état de l'application - effectuez des contrôles de l'état de santé de chaque service et assurez-vous que les clients de ce service sont informés de leur mauvais état de santé. Les outils de conteneur modernes tels que Docker le font très bien et devraient être utilisés pour le traitement en bac à sable.
L'une des principales leçons que j'ai apprises en travaillant sur un système hautement concurrentiel est la suivante: vous ne pouvez jamais avoir assez de métriques . Les métriques doivent contrôler absolument tout dans votre application - vous n'êtes pas un ingénieur si vous ne mesurez pas tout.
Sans métriques, vous ne pouvez pas faire quelques choses très importantes:
Évaluez la différence apportée par les modifications apportées au système. Si vous ne savez pas si le bouton de réglage A fait monter la métrique B et la métrique C, vous ne savez pas comment réparer votre système lorsque des personnes poussent du code inopinément malin sur votre système (et le feront ensuite sur votre système) .
Comprenez ce que vous devez faire ensuite pour améliorer les choses. Jusqu'à ce que vous sachiez que les applications manquent de mémoire, vous ne pouvez pas déterminer si vous devez obtenir plus de mémoire ou acheter plus de disque pour vos serveurs.
Les métriques sont tellement cruciales et essentielles que j'ai fait un effort conscient pour planifier ce que je veux mesurer avant même de penser à ce qu'un système nécessitera. En fait, les métriques sont tellement cruciales que je crois qu’elles sont la bonne réponse à cette question: vous ne savez que ce qui peut être fait de manière séquentielle ou simultanée lorsque vous mesurez ce que font les bits de votre programme. Une conception appropriée utilise des chiffres et non des conjectures.
Cela étant dit, il existe certainement quelques règles de base:
Séquentielle implique la dépendance. Deux processus doivent être séquentiels si l’un dépend de l’autre. Les processus sans dépendance doivent être simultanés. Cependant, prévoyez un moyen de gérer les échecs en amont qui n'empêche pas les processus en aval d'attendre indéfiniment.
Ne mélangez jamais une tâche liée aux E / S avec une tâche liée au processeur sur le même noyau. N'écrivez pas (par exemple) un robot d'exploration Web qui lance dix demandes simultanées dans le même fil, les récupère dès qu'elles arrivent et s'attend à passer à cinq cents - les demandes d'E / S sont placées dans une file d'attente en parallèle, mais la CPU les passera toujours en série. (Ce modèle événementiel mono-threadé est populaire, mais il est limité à cause de cet aspect - plutôt que de comprendre cela, les gens se tordent simplement la main et disent que Node n'échelle pas, pour vous donner un exemple).
Un seul thread peut faire beaucoup de travail d'E / S. Mais pour utiliser pleinement la simultanéité de votre matériel, utilisez des pools de threads qui occupent tous les cœurs. Dans l'exemple ci-dessus, le lancement de cinq processus Python (chacun pouvant utiliser un cœur sur une machine à six cœurs) juste pour le travail du processeur et un sixième thread Python juste pour le travail d'E / S évolueront beaucoup plus rapidement que vous ne le pensez.
La seule façon de tirer parti de la concurrence du processeur consiste à utiliser un pool de threads dédié. Un seul thread est souvent suffisant pour de nombreux travaux liés aux E / S. C’est pourquoi les serveurs Web événementiels tels que Nginx s’adaptent mieux (qu’ils effectuent un travail purement lié aux E / S) et Apache (qui confond le travail lié aux E / S avec quelque chose nécessitant un processeur et lance un processus par requête), mais pourquoi utiliser Node pour les exécuter des dizaines de milliers de calculs GPU reçus en parallèle est une idée terrible .
la source
Eh bien, pour le processus de vérification, lors de la conception d’un grand système concurrent, j’ai tendance à tester le modèle à l’aide de LTSA - Labeled Transition System Analyzer . Il a été développé par mon ancien tuteur, qui est un vétéran dans le domaine de la concurrence et qui est actuellement responsable de l'informatique à l'Impériale.
En ce qui concerne ce qui peut et ne peut pas être simultané, il existe des analyseurs statiques qui pourraient le montrer, bien que j'aie tendance à ne dessiner que des diagrammes de planification pour les sections critiques, comme vous le feriez pour la gestion de projet. Identifiez ensuite les sections qui effectuent la même opération de manière répétée. Une solution rapide consiste simplement à trouver des boucles, car ce sont elles qui bénéficient généralement du traitement en parallèle.
la source
Pratiquement tout ce que vous écrivez peut tirer parti de la concurrence, en particulier du cas d'utilisation "diviser une conquête". Une meilleure question est ce qui devrait être concurrente?
Le filetage en C # de Joseph Albahari énumère cinq utilisations courantes.
Si vous n'essayez pas de faire l'une des choses ci-dessus, vous feriez probablement mieux d'y réfléchir sérieusement.
Si vous utilisez .NET et que vous avez écrit des cas d'utilisation, vous pouvez utiliser CHESS, qui permet de recréer des conditions d'entrelacement de threads spécifiques, ce qui vous permet de tester votre correctif.
Ça dépend du contexte. Pour les scénarios de travailleur, je pense à un gestionnaire subordonné. Le gestionnaire demande au subordonné de faire quelque chose et attend les mises à jour de statut.
Pour des tâches concurrentes non liées, je pense aux ascenseurs ou aux voitures dans des voies de circulation séparées.
Pour la synchronisation, je pense parfois aux feux de circulation ou aux styles de virage.
De même, si vous utilisez C # 4.0, vous voudrez peut-être jeter un coup d’œil à la bibliothèque de tâches parallèle.
la source
Ma réponse à cette question est:
Tout d’abord, j’ai besoin de savoir pourquoi je devrais utiliser la concurrence, car j’ai découvert que les gens sont excités par l’idée qui sous-tend la concurrence, mais ne pensent pas toujours au problème qu’ils essaient de résoudre.
Si vous devez simuler une situation réelle, comme des files d'attente, des flux de travail, etc., vous devrez probablement utiliser une approche simultanée.
Maintenant que je sais que je devrais l'utiliser, il est temps d'analyser le compromis, si vous avez beaucoup de processus, vous pouvez penser à un temps système de communication, mais si vous devez créer de nouvelles, vous risquez de vous retrouver sans solution concurrente (problème de réanimation si alors.)
Je ne suis pas un expert en la matière, mais je pense que pour les systèmes concurrents, ce n'est pas la bonne approche. Une approche théorique doit être choisie, en recherchant les 4 impératifs de blocage dans des domaines critiques:
Chaîne circulaire
J'essaie d'abord d'identifier qui sont les participants aux interactions, puis comment communiquent-ils et avec qui. Enfin, les graphiques et les diagrammes d'interaction m'aident à visualiser. Mon bon vieux tableau blanc ne peut être battu par aucun autre type de média.
la source
Je serai franc. J'adore les outils. J'utilise beaucoup d'outils. Ma première étape consiste à tracer les chemins prévus pour le flux d’état. Ma prochaine étape consiste à essayer de déterminer si cela en vaut la peine ou si le flux d'informations requis rendra le code en série trop souvent. Ensuite, je vais essayer de rédiger des modèles simples. Celles-ci peuvent aller d'une pile de sculptures de cure-dents brutes à des exemples simples similaires en python. Ensuite, je regarde quelques-uns de mes livres préférés, comme le petit livre de sémaphores, et vois si quelqu'un a déjà trouvé une meilleure solution à mon problème.
Puis je commence à coder.
Je rigole. Un peu plus de recherche d'abord. J'aime m'asseoir avec un autre pirate informatique et parcourir l'exécution attendue du programme à un niveau élevé. Si des questions se posent, nous passons à un niveau inférieur. Il est important de savoir si quelqu'un d'autre comprend suffisamment votre solution pour la maintenir.
Enfin, je commence à coder. J'essaie de garder les choses très simples en premier. Juste le chemin du code, rien d'extraordinaire. Déplacer aussi peu que possible. Évitez les écritures. Évitez les lectures susceptibles d'entrer en conflit avec les écritures. Evitez surtout les écritures en conflit avec les écritures. Il est très facile de constater que vous en avez un nombre positivement toxique et que votre belle solution n’est soudainement qu’un petit peu plus qu’une approche en série effrénée en cache.
Une bonne règle consiste à utiliser des cadres partout où vous le pouvez. Si vous écrivez vous-même des composants de threads de base, tels que de bonnes structures de données synchronisées, ou des primitives synchro véritables, il est presque certain que vous allez vous faire sauter la jambe.
Enfin, des outils. Le débogage est très difficile. J'utilise valgrind \ callgrind sur Linux en conjonction avec PIN et sur des studios parallèles sous Windows. N'essayez pas de déboguer ce matériel à la main. Vous pouvez probablement. Mais vous souhaiterez probablement ne pas l'avoir fait. Dix heures à maîtriser des outils puissants et quelques bons modèles vous feront gagner des centaines d’heures plus tard.
Avant tout, travaillez progressivement. Travailler avec soin. N'écrivez pas de code simultané lorsque vous êtes fatigué. Ne l'écrivez pas si vous avez faim. En fait, si vous pouvez l'éviter, ne l'écrivez tout simplement pas. La concurrence est difficile, et j’ai constaté que de nombreuses applications l’énumérant en tant que fonctionnalité sont souvent livrées avec.
En résumé:
Début:
Think
Talk
Test
Écrire simplement
Lire
Test
Ecrire
Debug
GOTO Begin
la source