Je suis un assez bon programmeur, mon patron est également un assez bon programmeur. Bien qu'il semble sous-estimer certaines tâches telles que le multi-threading et à quel point cela peut être difficile (je trouve cela très difficile pour autre chose que de lancer quelques threads, d'attendre que tout soit terminé, puis de renvoyer les résultats).
Au moment où vous commencez à vous inquiéter des blocages et des conditions de course, je trouve cela très difficile, mais le patron ne semble pas l'apprécier - je ne pense pas qu'il l'ait jamais vue. Il suffit de taper un verrou sur c'est assez l'attitude.
Alors, comment puis-je le présenter ou expliquer pourquoi il pourrait sous-estimer les complexités de la concurrence, du parallélisme et du multi-threading? Ou peut-être que je me trompe?
Edit: Juste un peu sur ce qu’il a fait - parcourez une liste, pour chaque élément de cette liste, créez un thread qui exécute une commande de mise à jour de la base de données en fonction des informations contenues dans cet élément. Je ne sais pas comment il a contrôlé le nombre de threads exécutés en même temps, je suppose qu'il les a probablement ajoutés à une file d'attente s'il y en avait trop en cours d'exécution (il n'aurait pas utilisé de sémaphore).
la source
Réponses:
Si vous pouvez compter sur une expérience mathématique quelconque, montrez comment un flux d’exécution normal, essentiellement déterministe, devient non seulement non déterministe avec plusieurs threads, mais de manière exponentielle , car vous devez vous assurer que tout entrelacement possible d’instructions machine fera tout de même le bon choix. Un exemple simple de mise à jour perdue ou de situation de lecture incorrecte est souvent révélateur.
"Giflez un verrou" est la solution triviale ... elle résout tous vos problèmes si vous n'êtes pas préoccupé par la performance. Essayez d’illustrer l’impact sur les performances si, par exemple, Amazon devait verrouiller toute la côte est chaque fois qu’un citoyen d’Atlanta commande un livre!
la source
Le multi-threading est simple. Coder une application pour le multi-threading est très, très facile.
Il existe une astuce simple: utiliser une file d'attente de messages bien conçue (ne lancez pas la vôtre) pour transmettre les données entre les threads.
La partie difficile consiste à faire en sorte que plusieurs threads mettent à jour par magie un objet partagé d'une manière ou d'une autre. C'est à ce moment-là que cela devient sujet aux erreurs parce que les gens ne font pas attention aux conditions de course qui sont présentes.
Beaucoup de gens n'utilisent pas de files de messages et tentent de mettre à jour des objets partagés et de se créer des problèmes.
Ce qui devient difficile, c’est de concevoir un algorithme qui fonctionne bien lors de la transmission de données entre plusieurs files d’attente. C'est difficile. Mais la mécanique des threads co-existants (via des files d'attente partagées) est simple.
Notez également que les threads partagent des ressources d'E / S. Un programme lié aux E / S (par exemple, connexions réseau, opérations de fichier ou opérations de base de données) ne va probablement pas aller plus vite avec beaucoup de threads.
Si vous souhaitez illustrer le problème de mise à jour d'objet partagé, c'est simple. Asseyez-vous à travers la table avec un tas de cartes en papier. Écrivez une série de calculs simples - 4 ou 6 formules simples - avec beaucoup d’espace en bas de la page.
Voici le jeu. Chacun lit une formule, écrit une réponse et pose une carte avec la réponse.
Chacun de vous fera la moitié du travail, non? Vous avez terminé dans la moitié du temps, non?
Si votre patron ne pense pas beaucoup et commence tout juste, vous allez vous retrouver en conflit d'une manière ou d'une autre et écrire les réponses à la même formule. Cela n'a pas fonctionné parce qu'il y a une condition raciale inhérente entre vous lire avant d'écrire. Rien ne vous empêche de lire la même formule et d'écraser les réponses de chacun.
Il existe de très nombreuses manières de créer des conditions de concurrence critique avec des ressources mal verrouillées ou non verrouillées.
Si vous voulez éviter tous les conflits, vous coupez le papier en une pile de formules. Vous en retirez un de la file d'attente, notez la réponse et affichez les réponses. Aucun conflit, car vous lisez tous les deux à partir d'une file de messages à un seul lecteur.
la source
La programmation multithread est probablement la solution la plus difficile à la concurrence. C'est en gros une abstraction assez basse de ce que fait réellement la machine.
Un certain nombre d'approches, telles que le modèle d'acteur ou la mémoire transactionnelle (logicielle) , sont beaucoup plus simples. Ou travailler avec des structures de données immuables (telles que des listes et des arbres).
Généralement, une séparation appropriée des problèmes facilite le multi-threading. Quelque chose, qui est trop souvent oublié, lorsque les utilisateurs génèrent 20 threads, tous essayant de traiter le même tampon. Utilisez des réacteurs pour lesquels vous avez besoin de synchronisation et transmettez généralement des données entre différents travailleurs dotés de files de messages.
Si vous avez un verrou dans la logique de votre application, vous avez commis une erreur.
Donc oui, techniquement, le multi-threading est difficile.
"Gifler un verrou" est à peu près la solution la moins évolutive aux problèmes de simultanéité, et va à l'encontre de l'objectif même du multi-threading. Cela revient à rétablir un problème dans un modèle d'exécution non simultané. Plus vous le faites, plus il est probable qu’un seul thread soit exécuté à la fois (ou 0 dans une impasse). Il défait le but entier.
C'est comme si on disait "Il est facile de résoudre les problèmes du 3ème monde. Il suffit de lancer une bombe dessus". Ce n’est pas parce qu’il existe une solution triviale que le problème est trivial, car la qualité du résultat est importante.
Mais dans la pratique, la résolution de ces problèmes est aussi difficile que d’autres problèmes de programmation et se fait mieux avec des abstractions appropriées. Ce qui le rend assez facile en fait.
la source
Je pense qu'il y a un angle non technique à cette question - l'OMI est une question de confiance. On nous demande généralement de reproduire des applications complexes telles que - oh, je ne sais pas - Facebook par exemple. Je suis parvenu à la conclusion que si vous devez expliquer la complexité d'une tâche à un non-initié / à la direction, alors quelque chose est pourri au Danemark.
Même si d’autres programmeurs ninja peuvent s’acquitter de cette tâche en 5 minutes, vos estimations sont basées sur vos capacités personnelles. Votre interlocuteur devrait soit apprendre à faire confiance à votre opinion sur la question, soit embaucher quelqu'un dont le mot sera prêt à accepter.
Le défi ne consiste pas à relayer les implications techniques que les gens ont tendance à ignorer ou sont incapables de saisir par la conversation, mais à établir une relation de respect mutuel.
la source
Une simple expérience de pensée pour comprendre les impasses est le problème du " philosophe à manger ". La situation de Therac 25 est l’un des exemples que j’ai tendance à utiliser pour décrire les mauvaises conditions de course .
"Il suffit de taper dessus" est la mentalité de quelqu'un qui n'a pas rencontré de bogues difficiles avec le multi-threading. Et il est possible qu'il pense que vous exagérez la gravité de la situation (je ne le sais pas - il est possible de faire exploser des choses ou de tuer des gens avec des bugs de conditions de compétition, en particulier avec des logiciels embarqués qui aboutissent dans des voitures).
la source
Les applications concurrentes ne sont pas déterministes. Avec le peu de code global exceptionnellement faible reconnu par le programmeur comme vulnérable, vous ne contrôlez pas le moment où une partie d'un thread / processus s'exécute par rapport à une partie d'un autre thread. Le test est plus difficile, prend plus de temps et il est peu probable que tous les défauts liés à la simultanéité soient détectés. Les défauts, s’ils sont trouvés, sont souvent subtils et ne peuvent pas être reproduits de manière cohérente, ce qui rend leur réparation difficile.
Par conséquent, la seule application concurrente correcte est celle qui est prouvée, quelque chose de peu pratiqué dans le développement de logiciels. En conséquence, la réponse de S.Lot constitue le meilleur conseil général, car il est relativement facile de prouver que le passage du message est correct.
la source
Réponse courte en deux mots: OBSERVABLE NONDETERMINISM
Réponse longue: Cela dépend de l'approche que vous utilisez en matière de programmation simultanée, en fonction de votre problème. Dans le livre Concepts, techniques et modèles de programmation informatique , les auteurs expliquent clairement quatre approches pratiques principales pour écrire des programmes simultanés:
Maintenant, la plus simple de ces quatre approches en dehors de la programmation séquentielle évidente est la concurrence simultanée déclarative , car les programmes écrits en utilisant cette approche n’ont pas de non-déterminisme observable . En d'autres termes, il n'y a pas de situation de concurrence , car elle est simplement un comportement non déterministe observable.
Mais le manque de non-déterminisme observable signifie qu'il existe certains problèmes auxquels nous ne pouvons pas nous attaquer à l'aide de la simultanéité déclarative. Voici où les deux dernières approches pas si faciles entrent en jeu. La partie pas si facile est une conséquence du non déterminisme observable. Maintenant, ils tombent tous les deux dans un modèle concomitant à états et ont également une expressivité équivalente. Mais en raison du nombre toujours croissant de cœurs par processeur, il semble que l'industrie se soit davantage intéressée récemment à la concurrence d'accès aux messages, comme en témoigne l'essor des bibliothèques de transmission de messages (par exemple Akka pour JVM) ou des langages de programmation (par exemple Erlang ). .
La bibliothèque Akka mentionnée précédemment, qui s'appuie sur un modèle théorique Actor, simplifie la construction d'applications simultanées, car vous n'avez plus à vous soucier des verrous, des écrans ou des transactions. D'un autre côté, cela nécessite une approche différente de la conception de la solution, c'est-à-dire une manière de hiérarchiser les acteurs composites. On pourrait dire que cela nécessite un état d'esprit totalement différent, ce qui peut encore être finalement plus difficile que d'utiliser une concurrence simultanée partagée.
La programmation concurrente est difficile en raison du non-déterminisme observable, mais lorsque vous utilisez la bonne approche pour le problème donné et la bonne bibliothèque qui prend en charge cette approche, beaucoup de problèmes peuvent être évités.
la source
On m'a d'abord appris qu'il pouvait poser problème en voyant un programme simple qui démarrait deux threads et les faisait imprimer tous les deux sur la console en même temps de 1 à 100. Au lieu de:
Vous obtenez quelque chose de plus comme ça:
Exécutez-le à nouveau et vous obtiendrez des résultats totalement différents.
La plupart d'entre nous ont été formés pour supposer que notre code sera exécuté de manière séquentielle. Avec la plupart des multi-threads, nous ne pouvons pas prendre cela pour acquis "out of the box".
la source
Essayez d’utiliser plusieurs marteaux pour écraser un groupe de clous très rapprochés en même temps, sans aucune communication entre ceux qui tiennent les marteaux ... (supposons qu’ils ont les yeux bandés).
Escalader ceci pour construire une maison.
Il ne vous reste plus qu'à dormir la nuit en vous imaginant architecte. :)
la source
Partie facile: utilisez le multithreading avec les fonctionnalités modernes des frameworks, des systèmes d’exploitation et du matériel, telles que les sémaphores, les files d’attente, les compteurs verrouillés, les types de boîtes atomiques, etc.
Partie difficile: implémenter les fonctionnalités elles-mêmes en n'utilisant aucune fonctionnalité en premier lieu, peut être sauf quelques fonctionnalités matérielles très limitées, en s'appuyant uniquement sur des garanties de cohérence d'horloge sur plusieurs cœurs.
la source