{"id":3775,"date":"2013-08-19T08:00:45","date_gmt":"2013-08-19T07:00:45","guid":{"rendered":"http:\/\/www.blaess.fr\/christophe\/?p=3775"},"modified":"2013-08-19T21:53:52","modified_gmt":"2013-08-19T20:53:52","slug":"notifications-rapides-dune-variable-condition","status":"publish","type":"post","link":"https:\/\/www.blaess.fr\/christophe\/2013\/08\/19\/notifications-rapides-dune-variable-condition\/","title":{"rendered":"Notifications rapides d&rsquo;une variable-condition"},"content":{"rendered":"<p style=\"text-align: justify;\">Un client m&rsquo;a interrog\u00e9 r\u00e9cemment sur la mise en \u0153uvre d&rsquo;une notification d&rsquo;\u00e9v\u00e9nements entre threads. Je l&rsquo;ai tout naturellement aiguill\u00e9 vers l&#8217;emploi d&rsquo;une variable condition <code>pthread_cond_t<\/code>. Toutefois lorsque les notifications se sont produites par rafales rapides, des probl\u00e8mes se sont pos\u00e9s, que je n&rsquo;ai pu r\u00e9soudre qu&rsquo;avec l&#8217;emploi d&rsquo;un s\u00e9maphore suppl\u00e9mentaire.<\/p>\n<p style=\"text-align: justify;\">\n<!--more-->\n<\/p>\n<h1>Contexte<\/h1>\n<p style=\"text-align: justify;\">Un programme industriel re\u00e7oit des donn\u00e9es en provenance d&rsquo;un port de communication RS-485. Lorsqu&rsquo;une trame compl\u00e8te est re\u00e7ue, des op\u00e9rations doivent \u00eatre r\u00e9alis\u00e9es n\u00e9cessitant des \u00e9missions de donn\u00e9es, des attentes \u00e9ventuelles, ceci en parall\u00e8le sur la r\u00e9ception de la trame suivante. J&rsquo;ai donc propos\u00e9 \u00e0 mon client de concevoir cette fonctionnalit\u00e9 avec deux threads, l&rsquo;un se chargeant de recevoir les donn\u00e9es, puis de notifier le second afin qu&rsquo;il traite la suite des op\u00e9rations.<\/p>\n<p style=\"text-align: justify;\">Pour assurer la synchronisation entre les deux, il m&rsquo;a sembl\u00e9 judicieux d&rsquo;utiliser une <strong>variable-condition<\/strong>.<\/p>\n<h1>Variable condition<\/h1>\n<p style=\"text-align: justify;\">Une variable-condition est simplement une donn\u00e9e de type <code>pthread_cond_t<\/code> partag\u00e9e entre deux threads. J&rsquo;ai pour habitude, lorsque je pr\u00e9sente cette structure de synchronisation durant une session de formation, de la comparer \u00e0 une cloche. Un thread peut s&rsquo;endormir, passivement, en attente sur la cloche et un autre thread peut venir \u00e0 tout moment le r\u00e9veiller en donnant un coup de marteau sur la cloche.<\/p>\n<p style=\"text-align: justify;\">Si aucun thread n&rsquo;est attente au moment du coup de marteau, tant pis, cette notification est perdue.<\/p>\n<p style=\"text-align: justify;\">C&rsquo;est pour \u00e9viter cela, que l&rsquo;on associe toujours une variable condition avec un mutex <code>pthread_mutex_t<\/code>. Ainsi, on pourra garantir qu&rsquo;au moment du coup de marteau, un thread sera syst\u00e9matiquement en attente d&rsquo;un r\u00e9veil.<\/p>\n<p style=\"text-align: justify;\">Voyons un exemple d&rsquo;utilisation. Le thread qui attend les notifications se pr\u00e9sente g\u00e9n\u00e9ralement ainsi.<\/p>\n<pre>    pthread_cond_t cnd = PTHREAD_COND_INITIALIZER;\n    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;\n\nint main(void)\n{\n    \/\/ ... initialisation ...\n    pthread_mutex_lock(&amp; mtx);\n\n    while (1) {\n        pthread_cond_wait(&amp; cnd, &amp; mtx);\n        \/\/ notification recue, traitement...\n        \/\/ ...\n    }\n\n    pthread_mutex_unlock(&amp; mtx);\n    return EXIT_SUCCESS;\n}<\/pre>\n<p style=\"text-align: justify;\">Le second thread, qui doit notifier le premier travaille ainsi&nbsp;:<\/p>\n<pre>void * fonction_thread (void * arg)\n{\n\twhile (1) {\n\t\t\/\/ Attendre les donn\u00e9es externes\n\t\t\/\/ ....\n\t\t\/\/ Notifier le thread main()\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t }\n}<\/pre>\n<p style=\"text-align: justify;\">Contrairement \u00e0 ce que l&rsquo;on pourrait croire au premier regard, il n&rsquo;y a pas d&rsquo;erreur dans le code ci-dessus, il y a bien deux <code>pthread_mutex_lock()<\/code> invoqu\u00e9s dans les deux threads. Ce qu&rsquo;il faut comprendre c&rsquo;est que <code>pthread_cond_wait()<\/code> contient plusieurs \u00e9tapes successives&nbsp;:<\/p>\n<ul>\n<li style=\"text-align: justify;\">rel\u00e2chement du mutex (comme avec <code>pthread_mutex_unlock()<\/code>)\u00a0;<\/li>\n<li style=\"text-align: justify;\">mise en sommeil en attente de notification\u00a0;<\/li>\n<li style=\"text-align: justify;\">reprise du mutex (comme <code>pthread_mutex_lock()<\/code>).<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">L&rsquo;astuce est que les deux premiers points sont atomiquement li\u00e9s. Ainsi, il n&rsquo;est pas possible qu&rsquo;un thread invoque <code>pthread_cond_signal()<\/code> sans que le premier ne soit v\u00e9ritablement en attente.<\/p>\n<p style=\"text-align: justify; padding-left: 30px;\">NB: On notera que <code>pthread_cond_wait()<\/code> pr\u00e9sente \u00e9galement deux autres particularit\u00e9s inattendues&nbsp;: la possibilit\u00e9 que le thread soit r\u00e9veill\u00e9 pr\u00e9matur\u00e9ment sans qu&rsquo;une notification ne soit survenue, et le fait qu&rsquo;il s&rsquo;agisse d&rsquo;un <i>point d&rsquo;annulation<\/i> si <code>pthread_cancel()<\/code> est invoqu\u00e9 dans un autre thread. Ceci d\u00e9passe le cadre de cet article et n&rsquo;a pas d&rsquo;importance pour le sujet trait\u00e9 ici.<\/p>\n<h1>Premier essai<\/h1>\n<p style=\"text-align: justify;\">Dans ce premier exemple, le thread principal cr\u00e9e un thread de notification puis se met en attente sur la variable-condition. Les notifications se produiront r\u00e9guli\u00e8rement toutes les secondes. Les deux threads d\u00e9criront leurs progressions sur la sortie d&rsquo;erreur.<\/p>\n<pre><strong>exemple-condition-01.c<\/strong>:\n#include &lt;pthread.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys\/time.h&gt;\n\npthread_cond_t  cnd = PTHREAD_COND_INITIALIZER;\npthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;\n\nvoid * thread_notification (void * arg)\n{\n\tstruct timeval tv;\n\twhile (1) {\n\t\tsleep(1);\n\n\t\t<strong>pthread_mutex_lock(&amp; mtx);<\/strong>\n\t\tgettimeofday(&amp; tv, NULL);\n\t\t<strong>pthread_cond_signal(&amp; cnd);<\/strong>\n\t\t<strong>pthread_mutex_unlock(&amp; mtx);<\/strong>\n\t\tfprintf(stderr, \"Notification envoy\u00e9e \u00e0: %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);\n\t}\n\treturn NULL;\n}\n\nint main (void)\n{\n\tpthread_t thr;\n\tstruct timeval tv;\n\n\t<strong>pthread_mutex_lock(&amp; mtx);<\/strong>\n\tif (pthread_create(&amp; thr, NULL, thread_notification, NULL) != 0)\n\t\texit(EXIT_FAILURE);\n\twhile (1) {\n\t\t<strong>pthread_cond_wait(&amp; cnd, &amp; mtx);<\/strong>\n\t\tgettimeofday(&amp; tv, NULL);\n\t\tfprintf(stderr, \"Notification re\u00e7ue \u00e0 %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);\n\t}\n\t<strong>pthread_mutex_unlock(&amp; mtx);<\/strong>\n\treturn EXIT_SUCCESS;\t\n}<\/pre>\n<p style=\"text-align: justify;\">L&rsquo;ex\u00e9cution semble correcte.<\/p>\n<pre>$ <strong>.\/exemple-condition-01 <\/strong>\nNotification envoy\u00e9e \u00e0: 1376546517.395413\nNotification re\u00e7ue \u00e0 1376546517.395442\nNotification envoy\u00e9e \u00e0: 1376546518.395572\nNotification re\u00e7ue \u00e0 1376546518.395599\nNotification envoy\u00e9e \u00e0: 1376546519.395732\nNotification re\u00e7ue \u00e0 1376546519.395761\nNotification envoy\u00e9e \u00e0: 1376546520.395891\nNotification re\u00e7ue \u00e0 1376546520.395920\nNotification envoy\u00e9e \u00e0: 1376546521.396049\nNotification re\u00e7ue \u00e0 1376546521.396077\nNotification envoy\u00e9e \u00e0: 1376546522.396267\nNotification re\u00e7ue \u00e0 1376546522.396295\nNotification envoy\u00e9e \u00e0: 1376546523.396430\nNotification re\u00e7ue \u00e0 1376546523.396460\nNotification envoy\u00e9e \u00e0: 1376546524.396594\nNotification re\u00e7ue \u00e0 1376546524.396624\n^C\n$<\/pre>\n<h1>Rafales de notifications<\/h1>\n<p style=\"text-align: justify;\">V\u00e9rifions ce qui se produit si plusieurs notifications sont envoy\u00e9es rapidement. Pour cela nous allons remplacer, dans le thread, la s\u00e9quence<\/p>\n<pre>\t\tpthread_mutex_lock(&amp; mtx);\n\t\tgettimeofday(&amp; tv, NULL);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\tfprintf(stderr, \"Notification envoy\u00e9e \u00e0: %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);<\/pre>\n<p style=\"text-align: justify;\">par<\/p>\n<pre>\t\tpthread_mutex_lock(&amp; mtx);\n\t\tgettimeofday(&amp; tv, NULL);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\n\t\tfprintf(stderr, \"Trois notifications envoy\u00e9es \u00e0: %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);<\/pre>\n<p style=\"text-align: justify;\">L&rsquo;ex\u00e9cution est beaucoup moins bonne qu&rsquo;auparavant.<\/p>\n<pre>$ <strong>.\/exemple-condition-02<\/strong>\nTrois notifications envoy\u00e9es \u00e0: 1376546967.599002\nNotification re\u00e7ue \u00e0 1376546967.599041\nTrois notifications envoy\u00e9es \u00e0: 1376546968.599216\nNotification re\u00e7ue \u00e0 1376546968.599246\nTrois notifications envoy\u00e9es \u00e0: 1376546969.599377\nNotification re\u00e7ue \u00e0 1376546969.599406\nTrois notifications envoy\u00e9es \u00e0: 1376546970.599540\nNotification re\u00e7ue \u00e0 1376546970.599567\n^C\n$<\/pre>\n<p style=\"text-align: justify;\">Bien que nous envoyions trois notifications \u00e0 chaque fois, une seule d&rsquo;entre elles est re\u00e7ue par le thread principal.<\/p>\n<p>Que se passe-t-il&nbsp;?<\/p>\n<ul>\n<li style=\"text-align: justify;\">Le thread <em>main<\/em> est endormi sur l&rsquo;appel <code>pthread_cond_wait()<\/code>. Il a rel\u00e2ch\u00e9 le mutex.<\/li>\n<li style=\"text-align: justify;\">Le thread de notification prend le mutex.<\/li>\n<li style=\"text-align: justify;\">Le thread de notification invoque <code>pthread_cond_signal()<\/code>. Cela r\u00e9veille le thread principal, qui essaye de reprendre le mutex. Celui-ci n&rsquo;\u00e9tant pas libre, le thread <em>main<\/em> se rendort en attente du mutex.<\/li>\n<li style=\"text-align: justify;\">Le thread de notification l\u00e2che le mutex. Cet appel va r\u00e9veiller le thread principal. N\u00e9anmoins ce dernier ne r\u00e9cup\u00e8re pas instantan\u00e9ment le mutex, il faudra pour cela qu&rsquo;il soit s\u00e9lectionn\u00e9 par l&rsquo;ordonnanceur et puisse s&rsquo;ex\u00e9cuter.<\/li>\n<li style=\"text-align: justify;\">L&rsquo;ordonnanceur n&rsquo;ayant aucune raison de pr\u00e9empter le thread de notification au profit du thread principal, ce dernier reste en attente (dans l&rsquo;\u00e9tat \u00ab\u00a0Pr\u00eat\u00a0\u00bb) alors que le premier peut continuer son ex\u00e9cution et reprendre \u00e0 nouveau le mutex.<\/li>\n<li style=\"text-align: justify;\">Le thread de notification peut appeler une seconde fois <code>pthread_cond_signal()<\/code>. Puis la m\u00eame s\u00e9quence d&rsquo;op\u00e9rations se reproduit une seconde fois, et ce n&rsquo;est que lorsque le thread de notification s&rsquo;endort explicitement avec <code>sleep() <\/code>que le thread principal est activ\u00e9 et peut sortir du <code>pthread_cond_wait()<\/code>.<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Trois notifications ont \u00e9t\u00e9 envoy\u00e9es. Une seule a \u00e9t\u00e9 d\u00e9tect\u00e9e. Ce n&rsquo;est pas un comportement tr\u00e8s fiable&nbsp;!<\/p>\n<h1>Am\u00e9lioration<\/h1>\n<p style=\"text-align: justify;\">J&rsquo;avais d\u00e9j\u00e0 explor\u00e9 un probl\u00e8me assez similaire en octobre 2011 dans l&rsquo;article <a title=\"Prises de mutex et priorit\u00e9s\" href=\"http:\/\/www.blaess.fr\/christophe\/2011\/10\/22\/prises-de-mutex-et-priorites\/\" target=\"_blank\">Prise de mutex et priorit\u00e9s<\/a>.<\/p>\n<p style=\"text-align: justify;\">Nous avions vu alors que pour r\u00e9soudre le probl\u00e8me de reprise de mutex, il \u00e9tait possible de s&rsquo;appuyer sur l&rsquo;appel syst\u00e8me <code>sched_yield()<\/code> qui repr\u00e9sente un appel direct au <i>scheduler<\/i> en se pla\u00e7ant volontairement dans une situation d&rsquo;ordonnancement d\u00e9favorable.<\/p>\n<p style=\"text-align: justify;\">Nous allons donc tenter d&rsquo;am\u00e9liorer notre syst\u00e8me en ajoutant deux choses.<\/p>\n<ul>\n<li style=\"text-align: justify;\">Une priorit\u00e9 temps r\u00e9el pour le thread principal plus \u00e9lev\u00e9e que la priorit\u00e9 temps partag\u00e9 du thread de notification\u00a0;<\/li>\n<li style=\"text-align: justify;\">Des appels syst\u00e9matiques <code>sched_yield()<\/code> apr\u00e8s le <code>pthread_mutex_unlock() <\/code>qui suit le <code>pthread_cond_signal()<\/code>.<\/li>\n<\/ul>\n<pre><strong>exemple-condition-03.c<\/strong>\n\n#include &lt;pthread.h&gt;\n#include &lt;sched.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys\/time.h&gt;\n\npthread_cond_t  cnd = PTHREAD_COND_INITIALIZER;\npthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;\n\nvoid * thread_notification (void * arg)\n{\n\tstruct timeval tv;\n\twhile (1) {\n\t\tsleep(1);\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tgettimeofday(&amp; tv, NULL);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\t<strong>sched_yield();<\/strong>\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\t<strong>sched_yield();<\/strong>\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\t<strong>sched_yield();<\/strong>\n\n\t\tfprintf(stderr, \"Trois notifications envoy\u00e9es \u00e0: %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);\n\n\t}\n\treturn NULL;\t\n}\n\nint main (void)\n{\n\tpthread_t thr;\n\tstruct timeval tv;\n\t<strong>struct sched_param param;<\/strong>\n\n\tpthread_mutex_lock(&amp; mtx);\n\tif (pthread_create(&amp; thr, NULL, thread_notification, NULL) != 0)\n\t\texit(EXIT_FAILURE);\n\n\t<strong>param.sched_priority = 10;<\/strong>\n\tif (<strong>pthread_setschedparam(pthread_self(), SCHED_FIFO, &amp; param)<\/strong> != 0) {\n\t\tperror(\"pthread_setschedparam\");\n\t\texit(EXIT_FAILURE);\n\t}\n\n\twhile (1) {\n\t\tpthread_cond_wait(&amp; cnd, &amp; mtx);\n\t\tgettimeofday(&amp; tv, NULL);\n\t\tfprintf(stderr, \"Notification re\u00e7ue \u00e0 %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);\n\t}\n\tpthread_mutex_unlock(&amp; mtx);\n\treturn EXIT_SUCCESS;\t\n}<\/pre>\n<p>Le premier inconv\u00e9nient de ce programme, c&rsquo;est la n\u00e9cessit\u00e9 de l&rsquo;ex\u00e9cuter avec les droits <i>root<\/i> afin qu&rsquo;il puisse prendre un ordonnancement temps r\u00e9el.<\/p>\n<p style=\"text-align: justify;\">De plus, l&rsquo;ex\u00e9cution n&rsquo;est pas vraiment plus concluante que le pr\u00e9c\u00e9dent.<\/p>\n<pre>$ <strong>sudo .\/exemple-condition-03 <\/strong>\nTrois notifications envoy\u00e9es \u00e0: 1376554376.792935\nNotification re\u00e7ue \u00e0 1376554376.792975\nTrois notifications envoy\u00e9es \u00e0: 1376554377.793192\nNotification re\u00e7ue \u00e0 1376554377.793307\nTrois notifications envoy\u00e9es \u00e0: 1376554378.793395\nNotification re\u00e7ue \u00e0 1376554378.793428\nTrois notifications envoy\u00e9es \u00e0: 1376554379.793623\nNotification re\u00e7ue \u00e0 1376554379.793654\nTrois notifications envoy\u00e9es \u00e0: 1376554380.793855\nNotification re\u00e7ue \u00e0 1376554380.793886\nTrois notifications envoy\u00e9es \u00e0: 1376554381.794084\nNotification re\u00e7ue \u00e0 1376554381.794117\nTrois notifications envoy\u00e9es \u00e0: 1376554382.794310\nNotification re\u00e7ue \u00e0 1376554382.794350\nTrois notifications envoy\u00e9es \u00e0: 1376554383.794489\nNotification re\u00e7ue \u00e0 1376554383.794519\nTrois notifications envoy\u00e9es \u00e0: 1376554384.794717\nNotification re\u00e7ue \u00e0 1376554384.794750<\/pre>\n<p style=\"text-align: justify;\">Aucune des triples notifications n&rsquo;a \u00e9t\u00e9 re\u00e7ue en plus d&rsquo;un exemplaire.<\/p>\n<p style=\"text-align: justify;\">Pourtant, si on laisse le programme tourner un moment, surtout si la charge syst\u00e8me augmente un peu, voici ce que l&rsquo;on constate.<\/p>\n<pre>Trois notifications envoy\u00e9es \u00e0: 1376554386.795122\nNotification re\u00e7ue \u00e0 1376554387.795484\nNotification re\u00e7ue \u00e0 1376554387.795570\nNotification re\u00e7ue \u00e0 1376554387.795632\nTrois notifications envoy\u00e9es \u00e0: 1376554387.795452\nTrois notifications envoy\u00e9es \u00e0: 1376554388.795782\nNotification re\u00e7ue \u00e0 1376554388.795809\nNotification re\u00e7ue \u00e0 1376554389.795982\nTrois notifications envoy\u00e9es \u00e0: 1376554389.795960\nNotification re\u00e7ue \u00e0 1376554390.796222\nTrois notifications envoy\u00e9es \u00e0: 1376554390.796203\nNotification re\u00e7ue \u00e0 1376554391.796478\nNotification re\u00e7ue \u00e0 1376554391.796548\nTrois notifications envoy\u00e9es \u00e0: 1376554391.796461\nNotification re\u00e7ue \u00e0 1376554392.796768\nTrois notifications envoy\u00e9es \u00e0: 1376554392.796748\nNotification re\u00e7ue \u00e0 1376554393.797030\nTrois notifications envoy\u00e9es \u00e0: 1376554393.797011\nNotification re\u00e7ue \u00e0 1376554394.797294\nTrois notifications envoy\u00e9es \u00e0: 1376554394.797275<\/pre>\n<p style=\"text-align: justify;\">Dans certaines circonstances, il arrive que les notifications se comportent comme nous l&rsquo;attendions. Pourtant cela ne se produit que de temps \u00e0 autres, de mani\u00e8re a priori impr\u00e9visible.<\/p>\n<p style=\"text-align: justify;\">Ce genre de comportement apparement peu d\u00e9terministe doit nous faire penser \u00e0 des probl\u00e8mes d&rsquo;ex\u00e9cutions parall\u00e8les, plus particuli\u00e8rement sur les syst\u00e8mes multic\u0153urs (ou multiprocesseurs).<\/p>\n<p style=\"text-align: justify;\">L&rsquo;ex\u00e9cution pr\u00e9c\u00e9dente se d\u00e9roulait sur un petit processeur \u00e0 deux c\u0153urs. Essayons de forcer l&rsquo;ex\u00e9cution du programme sur un seul CPU.<\/p>\n<pre>$ <strong>sudo taskset -c 0 .\/exemple-condition-03<\/strong>\nNotification re\u00e7ue \u00e0 1376555394.075277\nNotification re\u00e7ue \u00e0 1376555394.075430\nNotification re\u00e7ue \u00e0 1376555394.075463\nTrois notifications envoy\u00e9es \u00e0: 1376555394.075235\nNotification re\u00e7ue \u00e0 1376555395.075686\nNotification re\u00e7ue \u00e0 1376555395.075776\nNotification re\u00e7ue \u00e0 1376555395.075832\nTrois notifications envoy\u00e9es \u00e0: 1376555395.075653\nNotification re\u00e7ue \u00e0 1376555396.076056\nNotification re\u00e7ue \u00e0 1376555396.076171\nNotification re\u00e7ue \u00e0 1376555396.076204\nTrois notifications envoy\u00e9es \u00e0: 1376555396.076025\nNotification re\u00e7ue \u00e0 1376555397.076408\nNotification re\u00e7ue \u00e0 1376555397.076497\nNotification re\u00e7ue \u00e0 1376555397.076527\nTrois notifications envoy\u00e9es \u00e0: 1376555397.076370\nNotification re\u00e7ue \u00e0 1376555398.076710\nNotification re\u00e7ue \u00e0 1376555398.076795\nNotification re\u00e7ue \u00e0 1376555398.076853\nTrois notifications envoy\u00e9es \u00e0: 1376555398.076676\nNotification re\u00e7ue \u00e0 1376555399.077044\nNotification re\u00e7ue \u00e0 1376555399.077135\nNotification re\u00e7ue \u00e0 1376555399.077166\nTrois notifications envoy\u00e9es \u00e0: 1376555399.077010<\/pre>\n<p style=\"text-align: justify;\">Le thread principal \u00e9tant plus prioritaire, c&rsquo;est lui qui affiche d&rsquo;abord ses messages (\u00ab\u00a0Notification re\u00e7ue\u00a0\u00bb) avant que le thread de notification affiche le sien, n\u00e9anmoins nous pouvons v\u00e9rifier \u00e0 l&rsquo;aide des horodatages que l&rsquo;ex\u00e9cution est coh\u00e9rente.<\/p>\n<p style=\"text-align: justify;\">Dans la situation o\u00f9 notre syst\u00e8me fonctionne sur un processeur unic\u0153ur, la solution <code>sched_yield()<\/code> et priorit\u00e9 temps r\u00e9el est suffisante pour assurer le bon d\u00e9roulement du programme. Chaque fois que le thread de notification invoque <code>sched_yield()<\/code>, l&rsquo;ordonnanceur laisse l&rsquo;ex\u00e9cution au thread principal qui peut obtenir le mutex et traiter la notification.<\/p>\n<p style=\"text-align: justify;\">Toutefois ceci ne fonctionne donc pas sur un processeur multic\u0153ur si les deux threads tournent sur deux CPU diff\u00e9rents (cas habituel lorsque le syst\u00e8me n&rsquo;est pas trop charg\u00e9)&nbsp;: lorsque le thread de notification appelle <code>sched_yield()<\/code>, l&rsquo;ordonnanceur voit qu&rsquo;il est seul sur son CPU et le r\u00e9active imm\u00e9diatement ce qui lui permet de reverrouiller le mutex avant que le thread principal ait eu le temps de se r\u00e9veiller.<\/p>\n<p style=\"text-align: justify;\">Cette solution n&rsquo;est donc pas satisfaisante. Il nous faudrait un moyen de s&rsquo;assurer que le thread principal a bien trait\u00e9 toutes les notifications ant\u00e9rieures avant de lui en renvoyer une nouvelle. Pour cela nous pouvons essayer d&rsquo;utiliser un s\u00e9maphore.<\/p>\n<h1>Emploi d&rsquo;un s\u00e9maphore<\/h1>\n<p style=\"text-align: justify;\">Un <strong>s\u00e9maphore<\/strong> est un objet de synchronisation dot\u00e9 d&rsquo;un compteur que l&rsquo;on peut incr\u00e9menter (avec la fonction <code>sem_post()<\/code>) ou d\u00e9cr\u00e9menter (avec <code>sem_wait()<\/code>). Le compteur doit toujours rester positif ou nul. La d\u00e9cr\u00e9mentation du compteur est une op\u00e9ration potentiellement bloquante tant que le compteur est nul.<\/p>\n<p style=\"text-align: justify;\">Contrairement aux objets de synchronisation comme les mutex, l&rsquo;incr\u00e9mentation et la d\u00e9cr\u00e9mentation d&rsquo;un s\u00e9maphore ne se produisent pas n\u00e9cessairement dans la m\u00eame t\u00e2che. Il est parfaitement usuel de voir un thread invoquer <code>sem_wait()<\/code> pour attendre une certaine circonstance qui lui sera indiqu\u00e9e par un autre thread invoquant <code>sem_post()<\/code>.<\/p>\n<p style=\"text-align: justify;\">Notre premi\u00e8re approche de l&rsquo;utilisation d&rsquo;un s\u00e9maphore sera donc en remplacement du <code>sched_yield()<\/code> pour attendre que le thread principal ait eu le temps de traiter la notification avant de lui en renvoyer une nouvelle.<\/p>\n<p>Voici un exemple de code.<\/p>\n<pre><strong>exemple-condition-04.c<\/strong>\n\n#include &lt;pthread.h&gt;\n#include &lt;semaphore.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys\/time.h&gt;\n\npthread_cond_t  cnd = PTHREAD_COND_INITIALIZER;\npthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;\n<strong>sem_t           sem;<\/strong>\n\nvoid * thread_notification (void * arg)\n{\n\tstruct timeval tv;\n\twhile (1) {\n\t\tsleep(1);\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tgettimeofday(&amp; tv, NULL);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\t<strong>sem_wait(&amp; sem);<\/strong>\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\t<strong>sem_wait(&amp; sem);<\/strong>\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\t<strong>sem_wait(&amp; sem);<\/strong>\n\n\t\tfprintf(stderr, \"Trois notifications envoy\u00e9es \u00e0: %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);\n\n\t}\n\treturn NULL;\n}\n\nint main (void)\n{\n\tpthread_t thr;\n\tstruct timeval tv;\n\n\t<strong>sem_init(&amp; sem, 0, 0);<\/strong>\n\n\tpthread_mutex_lock(&amp; mtx);\n\tif (pthread_create(&amp; thr, NULL, thread_notification, NULL) != 0)\n\t\texit(EXIT_FAILURE);\n\n\twhile (1) {\n\t\tpthread_cond_wait(&amp; cnd, &amp; mtx);\n\t\tgettimeofday(&amp; tv, NULL);\n\t\t<strong>sem_post(&amp; sem);<\/strong>\n\t\tfprintf(stderr, \"Notification re\u00e7ue \u00e0 %ld.%06ld\\n\", tv.tv_sec, tv.tv_usec);\n\t}\n\tpthread_mutex_unlock(&amp; mtx);\n\treturn EXIT_SUCCESS;\t\n}<\/pre>\n<p style=\"text-align: justify;\">Ainsi, le thread devra, apr\u00e8s avoir envoy\u00e9 une notification, attendre que le thread principal incr\u00e9mente le s\u00e9maphore (initialement nul) pour pouvoir continuer. V\u00e9rifions le fonctionnement.<\/p>\n<pre>$ <strong>.\/exemple-condition-04 <\/strong>\nNotification re\u00e7ue \u00e0 1376605365.617369\nNotification re\u00e7ue \u00e0 1376605365.617557\nNotification re\u00e7ue \u00e0 1376605365.617594\nTrois notifications envoy\u00e9es \u00e0: 1376605365.617332\nNotification re\u00e7ue \u00e0 1376605366.617822\nNotification re\u00e7ue \u00e0 1376605366.617897\nNotification re\u00e7ue \u00e0 1376605366.617988\nTrois notifications envoy\u00e9es \u00e0: 1376605366.617791\nNotification re\u00e7ue \u00e0 1376605367.618231\nNotification re\u00e7ue \u00e0 1376605367.618374\nNotification re\u00e7ue \u00e0 1376605367.618429\nTrois notifications envoy\u00e9es \u00e0: 1376605367.618200\nNotification re\u00e7ue \u00e0 1376605368.618599\nNotification re\u00e7ue \u00e0 1376605368.618679\nNotification re\u00e7ue \u00e0 1376605368.618763\nTrois notifications envoy\u00e9es \u00e0: 1376605368.618572\nNotification re\u00e7ue \u00e0 1376605369.618967\nNotification re\u00e7ue \u00e0 1376605369.619217\nNotification re\u00e7ue \u00e0 1376605369.619293\nTrois notifications envoy\u00e9es \u00e0: 1376605369.618938\nNotification re\u00e7ue \u00e0 1376605370.619516\nNotification re\u00e7ue \u00e0 1376605370.619593\nNotification re\u00e7ue \u00e0 1376605370.619682\nTrois notifications envoy\u00e9es \u00e0: 1376605370.619487\n^C\n$<\/pre>\n<p style=\"text-align: justify;\">Cette m\u00e9thode fonctionne tr\u00e8s bien dans notre cas. Aussi bien sur un syst\u00e8me multic\u0153ur que sur un processeur unic\u0153ur.<\/p>\n<h1>Notifications parall\u00e8les<\/h1>\n<p style=\"text-align: justify;\">Toutefois, en regardant le code propos\u00e9 \u00e0 mon client j&rsquo;ai \u00e9t\u00e9 pris d&rsquo;un doute. Dans l&rsquo;exemple ci-dessus, un seul thread envoyait des notifications au thread <i>main<\/i>, mais que se passera-t-il si plusieurs threads essayent d&rsquo;en envoyer simultan\u00e9ment&nbsp;?<\/p>\n<p style=\"text-align: justify;\">Supposons que deux threads, appelons-les A et B, entament simultan\u00e9ment la portion de code suivante&nbsp;:<\/p>\n<pre>  pthread_mutex_lock(&amp; mtx);\n  pthread_cond_signal(&amp; cnd);\n  pthread_mutex_unlock(&amp; amp; mtx);\n  sem_wait(&amp; sem);<\/pre>\n<p style=\"text-align: justify;\">Voici ce qui peut se produire&nbsp;:<\/p>\n<ul>\n<li style=\"text-align: justify;\">Le thread A prend le mutex.<\/li>\n<li style=\"text-align: justify;\">(Sur un autre CPU) le thread B r\u00e9clame le mutex. Ce dernier \u00e9tant d\u00e9j\u00e0 verrouill\u00e9, B reste bloqu\u00e9.<\/li>\n<li style=\"text-align: justify;\">A invoque <code>pthread_cond_signal()<\/code> qui r\u00e9veille le thread <em>main<\/em>. Celui-ci tente d&rsquo;obtenir le mutex (dans <code>pthread_cond_wait<\/code>), qui est verrouill\u00e9. Le thread <i>main<\/i> se rendort.<\/li>\n<li style=\"text-align: justify;\">A rel\u00e2che le mutex et se bloque en attente sur le s\u00e9maphore. La lib\u00e9ration du mutex r\u00e9veille B et <i>main<\/i>. L&rsquo;ordonnanceur choisit&#8230; B (par malchance).<\/li>\n<li style=\"text-align: justify;\">B obtient le mutex, et envoie la seconde notification sur la variable condition sans que le thread <i>main<\/i> n&rsquo;ait trait\u00e9 la premi\u00e8re.<\/li>\n<li style=\"text-align: justify;\">B rel\u00e2che le mutex et se bloque sur le s\u00e9maphore<\/li>\n<li style=\"text-align: justify;\">Le thread principal obtient enfin le mutex, fait un traitement et incr\u00e9mente <strong>une seule fois<\/strong> le s\u00e9maphore alors que deux threads sont bloqu\u00e9s en attente dessus.<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Non seulement notre programme rate des notifications, mais en outre des threads peuvent se retrouver d\u00e9finitivement bloqu\u00e9s par erreur&nbsp;!<\/p>\n<p style=\"text-align: justify;\">Pour v\u00e9rifier le comportement, j&rsquo;ai modifi\u00e9 quelque peu le programme pr\u00e9c\u00e9dent. Nous n&rsquo;affichons plus les dates de notification, mais la valeur de deux compteurs. L&rsquo;un est incr\u00e9ment\u00e9 \u00e0 chaque envoi de notification et le second \u00e0 chaque r\u00e9ception. Le thread principal affiche r\u00e9guli\u00e8rement ces deux compteurs. En outre, nous lan\u00e7ons cinq threads en parall\u00e8le.<\/p>\n<pre><strong>exemple-condition-05.c<\/strong>\n\n#include &lt;pthread.h&gt;\n#include &lt;semaphore.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys\/time.h&gt;\n\npthread_cond_t  cnd = PTHREAD_COND_INITIALIZER;\npthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;\nsem_t           sem;\n<strong>int compteur_notifications_envoyees = 0;<\/strong>\n<strong>int compteur_notifications_recues   = 0;<\/strong>\n\nvoid * thread_notification (void * arg)\n{\n\twhile (1) {\n\t\tsleep(1);\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\t<strong>compteur_notifications_envoyees ++;<\/strong>\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\tsem_wait(&amp; sem);\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\t<strong>compteur_notifications_envoyees ++;<\/strong>\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\tsem_wait(&amp; sem);\n\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\t<strong>compteur_notifications_envoyees ++;<\/strong>\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t\tsem_wait(&amp; sem);\n\t}\n\treturn NULL;\t\n}\n\n#define NB_THREADS 5\n\nint main (void)\n{\n\tpthread_t thr [NB_THREADS];\n\tint i;\n\n\tsem_init(&amp; sem, 0, 0);\n\n\tpthread_mutex_lock(&amp; mtx);\n\tfor (i = 0; i &lt; NB_THREADS; i ++) \n\t\tif (pthread_create(&amp; (thr[i]), NULL, thread_notification, NULL) != 0)\n\t\t\texit(EXIT_FAILURE);\n\n\twhile (1) {\n\t\tpthread_cond_wait(&amp; cnd, &amp; mtx);\n\t\t<strong>compteur_notifications_recues ++;<\/strong>\n\t\tsem_post(&amp; sem);\n\t\tfprintf(stderr, \"<strong>Notifications envoyees : %d, recues : %d<\/strong>\\n\",\n\t\t                compteur_notifications_envoyees,\n\t\t                compteur_notifications_recues);\n\t}\n\tpthread_mutex_unlock(&amp; mtx);\n\treturn EXIT_SUCCESS;\t\n}<\/pre>\n<p>L&rsquo;ex\u00e9cution confirme qu&rsquo;un probl\u00e8me se pose car les compteurs sont tr\u00e8s vite d\u00e9cal\u00e9s.<\/p>\n<pre>$ <strong>.\/exemple-condition-05<\/strong>\nNotifications envoyees : 1, recues : 1\nNotifications envoyees : 3, recues : 2\nNotifications envoyees : 7, recues : 3\nNotifications envoyees : 8, recues : 4\nNotifications envoyees : 9, recues : 5\nNotifications envoyees : 10, recues : 6\nNotifications envoyees : 11, recues : 7\nNotifications envoyees : 12, recues : 8\nNotifications envoyees : 13, recues : 9\nNotifications envoyees : 14, recues : 10\nNotifications envoyees : 15, recues : 11\nNotifications envoyees : 16, recues : 12\nNotifications envoyees : 17, recues : 13\nNotifications envoyees : 18, recues : 14\nNotifications envoyees : 19, recues : 15\nNotifications envoyees : 20, recues : 16\nNotifications envoyees : 21, recues : 17\nNotifications envoyees : 22, recues : 18\nNotifications envoyees : 23, recues : 19\nNotifications envoyees : 24, recues : 20\n^C\n$<\/pre>\n<p style=\"text-align: justify;\">D\u00e8s que les compteurs sont d\u00e9cal\u00e9s de 4 incr\u00e9mentations, nous pouvons imaginer que 4 threads sur les 5 sont bloqu\u00e9s sur le s\u00e9maphore.<\/p>\n<p style=\"text-align: justify;\">Comment pouvons-nous donc proc\u00e9der pour \u00eatre s\u00fbr que toutes les notifications envoy\u00e9s par les threads soient re\u00e7ues par le thread <i>main<\/i> ?<\/p>\n<p style=\"text-align: justify;\">Il existe une solution tr\u00e8s simple (et tr\u00e8s proche du programme ci-dessus). Je vous encourage \u00e0 y r\u00e9fl\u00e9chir quelques instants avant de\u00a0 poursuivre votre lecture.<\/p>\n<h1>Solution<\/h1>\n<p style=\"text-align: justify;\">Le probl\u00e8me qui se pose, nous l&rsquo;avons bien vu pr\u00e9c\u00e9demment est que deux threads peuvent entrer simultan\u00e9ment dans la portion de code<\/p>\n<pre>  pthread_mutex_lock(&amp; mtx);\n  pthread_cond_signal(&amp; cnd);\n  pthread_mutex_unlock(&amp; mtx);<\/pre>\n<p style=\"text-align: justify;\">Pour garantir l&rsquo;unicit\u00e9 d&rsquo;ex\u00e9cution d&rsquo;une portion de programme, nous pouvons justement faire appel \u00e0 un s\u00e9maphore. Au m\u00eame s\u00e9maphore que celui que nous utilisions pr\u00e9c\u00e9demment.<\/p>\n<p style=\"text-align: justify;\">Il suffit simplement de d\u00e9placer l&rsquo;appel <code>sem_wait()<\/code> <strong>avant<\/strong> la portion de code ci-dessus. Et sym\u00e9triquement d\u00e9placer <code>sem_post()<\/code> avant <code>pthread_cond_wait()<\/code>.<\/p>\n<p style=\"text-align: justify;\">Le s\u00e9maphore aura pour r\u00f4les de garantir<\/p>\n<ol>\n<li style=\"text-align: justify;\">qu&rsquo;un seul thread pourra ex\u00e9cuter la fonction de notification \u00e0 un moment donn\u00e9<\/li>\n<li style=\"text-align: justify;\">qu&rsquo;avant d&rsquo;ex\u00e9cuter la notification, nous sommes s\u00fbrs que le thread principal est en attente<\/li>\n<\/ol>\n<p style=\"text-align: justify;\">Voici donc le programme correct qui r\u00e9soud notre probl\u00e8me.<\/p>\n<pre><strong>exemple-condition-06.c<\/strong>\n\n#include &lt;pthread.h&gt;\n#include &lt;semaphore.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys\/time.h&gt;\n\npthread_cond_t  cnd = PTHREAD_COND_INITIALIZER;\npthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;\nsem_t           sem;\nint compteur_notifications_envoyees = 0;\nint compteur_notifications_recues   = 0;\n\nvoid * thread_notification (void * arg)\n{\n\twhile (1) {\n\t\tsleep(1);\n\n\t\t<strong>sem_wait(&amp; sem);<\/strong>\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tcompteur_notifications_envoyees ++;\n\t\tpthread_mutex_unlock(&amp; mtx);\n\n\t\t<strong>sem_wait(&amp; sem);<\/strong>\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tcompteur_notifications_envoyees ++;\n\t\tpthread_mutex_unlock(&amp; mtx);\n\n\t\t<strong>sem_wait(&amp; sem);<\/strong>\n\t\tpthread_mutex_lock(&amp; mtx);\n\t\tpthread_cond_signal(&amp; cnd);\n\t\tcompteur_notifications_envoyees ++;\n\t\tpthread_mutex_unlock(&amp; mtx);\n\t}\n\treturn NULL;\t\n}\n\n#define NB_THREADS 5\n\nint main (void)\n{\n\tpthread_t thr [NB_THREADS];\n\tint i;\n\n\tsem_init(&amp; sem, 0, 0);\n\n\tpthread_mutex_lock(&amp; mtx);\n\tfor (i = 0; i &lt; NB_THREADS; i ++) \n\t\tif (pthread_create(&amp; (thr[i]), NULL, thread_notification, NULL) != 0)\n\t\t\texit(EXIT_FAILURE);\n\n\twhile (1) {\n\t\t<strong>sem_post(&amp; sem);<\/strong>\n\t\tpthread_cond_wait(&amp; cnd, &amp; mtx);\n\t\tcompteur_notifications_recues ++;\n\t\tfprintf(stderr, \"Notifications envoyees : %d, recues : %d\\n\",\n\t\t                compteur_notifications_envoyees,\n\t\t                compteur_notifications_recues);\n\t}\n\tpthread_mutex_unlock(&amp; mtx);\n\treturn EXIT_SUCCESS;\t\n}<\/pre>\n<p style=\"text-align: justify; padding-left: 30px;\"><strong><\/strong>NB&nbsp;: J&rsquo;ai plac\u00e9 le <code>sem_wait()<\/code> avant le <code>pthread_mutex_lock()<\/code>. En effet, j&rsquo;essaye g\u00e9n\u00e9ralement d&rsquo;\u00e9viter les imbrications de m\u00e9canismes de synchronisation, de crainte du fameux <i>dead lock<\/i> si difficile \u00e0 d\u00e9buger. Je pense toutefois que le <code>sem_wait()<\/code> pourrait sans probl\u00e8me se trouver entre le <code>pthread_mutex_lock()<\/code> et le <code>pthread_cond_signal()<\/code>.<\/p>\n<p style=\"text-align: justify;\">V\u00e9rifions le fonctionnement&nbsp;:<\/p>\n<pre>$ <strong>.\/exemple-condition-06<\/strong>\nNotifications envoyees : 1, recues : 1\nNotifications envoyees : 2, recues : 2\nNotifications envoyees : 3, recues : 3\nNotifications envoyees : 4, recues : 4\nNotifications envoyees : 5, recues : 5\nNotifications envoyees : 6, recues : 6\nNotifications envoyees : 7, recues : 7\nNotifications envoyees : 8, recues : 8\nNotifications envoyees : 9, recues : 9\nNotifications envoyees : 10, recues : 10\nNotifications envoyees : 11, recues : 11\nNotifications envoyees : 12, recues : 12\nNotifications envoyees : 13, recues : 13\nNotifications envoyees : 14, recues : 14\nNotifications envoyees : 15, recues : 15\nNotifications envoyees : 16, recues : 16\nNotifications envoyees : 17, recues : 17\nNotifications envoyees : 18, recues : 18\nNotifications envoyees : 19, recues : 19\nNotifications envoyees : 20, recues : 20\nNotifications envoyees : 21, recues : 21\nNotifications envoyees : 22, recues : 22\nNotifications envoyees : 23, recues : 23\nNotifications envoyees : 24, recues : 24\nNotifications envoyees : 25, recues : 25\nNotifications envoyees : 26, recues : 26\nNotifications envoyees : 27, recues : 27\nNotifications envoyees : 28, recues : 28\nNotifications envoyees : 29, recues : 29\nNotifications envoyees : 30, recues : 30\nNotifications envoyees : 31, recues : 31\nNotifications envoyees : 32, recues : 32\nNotifications envoyees : 33, recues : 33\nNotifications envoyees : 34, recues : 34\nNotifications envoyees : 35, recues : 35\nNotifications envoyees : 36, recues : 36\nNotifications envoyees : 37, recues : 37\nNotifications envoyees : 38, recues : 38\nNotifications envoyees : 39, recues : 39\nNotifications envoyees : 40, recues : 40\nNotifications envoyees : 41, recues : 41\nNotifications envoyees : 42, recues : 42\nNotifications envoyees : 43, recues : 43\nNotifications envoyees : 44, recues : 44\nNotifications envoyees : 45, recues : 45\nNotifications envoyees : 46, recues : 46\nNotifications envoyees : 47, recues : 47\nNotifications envoyees : 48, recues : 48\nNotifications envoyees : 49, recues : 49\nNotifications envoyees : 50, recues : 50\nNotifications envoyees : 51, recues : 51\nNotifications envoyees : 52, recues : 52\nNotifications envoyees : 53, recues : 53\nNotifications envoyees : 54, recues : 54\nNotifications envoyees : 55, recues : 55\nNotifications envoyees : 56, recues : 56\nNotifications envoyees : 57, recues : 57\nNotifications envoyees : 58, recues : 58\nNotifications envoyees : 59, recues : 59\nNotifications envoyees : 60, recues : 60\nNotifications envoyees : 61, recues : 61\nNotifications envoyees : 62, recues : 62\nNotifications envoyees : 63, recues : 63\nNotifications envoyees : 64, recues : 64\nNotifications envoyees : 65, recues : 65\nNotifications envoyees : 66, recues : 66\nNotifications envoyees : 67, recues : 67\nNotifications envoyees : 68, recues : 68\nNotifications envoyees : 69, recues : 69\nNotifications envoyees : 70, recues : 70\nNotifications envoyees : 71, recues : 71\nNotifications envoyees : 72, recues : 72\nNotifications envoyees : 73, recues : 73\nNotifications envoyees : 74, recues : 74\nNotifications envoyees : 75, recues : 75\n^C\n$<\/pre>\n<h1>Conclusion<\/h1>\n<p style=\"text-align: justify;\">Nous avons r\u00e9ussi \u00e0 obtenir un sch\u00e9ma robuste et performant pour traiter dans un thread des notifications asynchrones d\u00e9clench\u00e9es par d&rsquo;autres threads. On voit que la solution n&rsquo;\u00e9tait pas si \u00e9vidente que nous pouvions le penser au d\u00e9but.<\/p>\n<p style=\"text-align: justify;\">Il faut comprendre que l&rsquo;API des Pthreads, con\u00e7ue dans les ann\u00e9es 1990, souffre de quelques d\u00e9fauts lorsqu&rsquo;on l&#8217;emploie avec des processeurs multic\u0153urs, qui n&rsquo;apparaissaient pas \u00e0 l&rsquo;\u00e9poque sur des syst\u00e8mes unic\u0153urs.<\/p>\n<p style=\"text-align: justify;\">Nous pouvons \u00e9galement noter que la solution reposant sur des priorit\u00e9s temps r\u00e9el ne fonctionne pas bien, et qu&rsquo;il est parfois n\u00e9cessaire d&rsquo;ajouter un objet de synchronisation suppl\u00e9mentaire pour venir \u00e0 bout des probl\u00e8mes pos\u00e9s par la concurrence d&rsquo;acc\u00e8s.<\/p>\n<p style=\"text-align: justify;\">L&rsquo;ensemble des fichiers sources et Makefile se trouvent dans <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2013-08-19.tar.bz2\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2013-08-19.tar.bz2\">cette archive<\/a>.<\/p>\n<p style=\"text-align: center;\">Remarques, commentaires, etc. sont les bienvenus&nbsp;!<\/p>","protected":false},"excerpt":{"rendered":"<p>Un client m&rsquo;a interrog&eacute; r&eacute;cemment sur la mise en &oelig;uvre d&rsquo;une notification d&rsquo;&eacute;v&eacute;nements entre threads. Je l&rsquo;ai tout naturellement aiguill&eacute; vers l&rsquo;emploi d&rsquo;une variable condition pthread_cond_t. Toutefois lorsque les notifications se sont produites par rafales rapides, des probl&egrave;mes se sont pos&eacute;s, que je n&rsquo;ai pu r&eacute;soudre qu&rsquo;avec l&rsquo;emploi d&rsquo;un s&eacute;maphore suppl&eacute;mentaire.<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,14],"tags":[],"class_list":["post-3775","post","type-post","status-publish","format-standard","hentry","category-linux-2","category-temps-reel"],"_links":{"self":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/3775","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/comments?post=3775"}],"version-history":[{"count":8,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/3775\/revisions"}],"predecessor-version":[{"id":3787,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/3775\/revisions\/3787"}],"wp:attachment":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/media?parent=3775"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/categories?post=3775"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/tags?post=3775"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}