146 votes

C++0x n'a pas de sémaphores ? Comment synchroniser les threads ?

Est-il vrai que le C++0x sera dépourvu de sémaphores ? Il y a déjà quelques questions sur Stack Overflow concernant l'utilisation des sémaphores. Je les utilise (sémaphores posix) tout le temps pour laisser un thread attendre un événement dans un autre thread :

void thread0(...)
{
  doSomething0();

  event1.wait();

  ...
}

void thread1(...)
{
  doSomething1();

  event1.post();

  ...
}

Si je faisais ça avec un mutex :

void thread0(...)
{
  doSomething0();

  event1.lock(); event1.unlock();

  ...
}

void thread1(...)
{
  event1.lock();

  doSomethingth1();

  event1.unlock();

  ...
}

Problème : c'est laid et il n'est pas garanti que le thread1 verrouille le mutex en premier (étant donné que le même thread doit verrouiller et déverrouiller un mutex, vous ne pouvez pas non plus verrouiller l'événement1 avant que le thread0 et le thread1 ne commencent).

Puisque boost n'a pas non plus de sémaphores, quel est le moyen le plus simple de réaliser ce qui précède ?

1 votes

Peut-être utiliser la condition mutex et std::promise et std::future ?

-5voto

Jeffery Points 19

Au cas où quelqu'un serait intéressé par la version atomique, voici l'implémentation. Les performances devraient être meilleures que celles de la version mutex & variable de condition.

class semaphore_atomic
{
public:
    void notify() {
        count_.fetch_add(1, std::memory_order_release);
    }

    void wait() {
        while (true) {
            int count = count_.load(std::memory_order_relaxed);
            if (count > 0) {
                if (count_.compare_exchange_weak(count, count-1, std::memory_order_acq_rel, std::memory_order_relaxed)) {
                    break;
                }
            }
        }
    }

    bool try_wait() {
        int count = count_.load(std::memory_order_relaxed);
        if (count > 0) {
            if (count_.compare_exchange_strong(count, count-1, std::memory_order_acq_rel, std::memory_order_relaxed)) {
                return true;
            }
        }
        return false;
    }
private:
    std::atomic_int count_{0};
};

0 votes

@DavidSchwartz Heureux de voir vos commentaires. Je ne suis pas sûr de comprendre la partie "...la prédiction de la boucle du CPU...". Je suis d'accord avec la deuxième partie. Apparemment, votre troisième cas peut se produire, mais comparé à mutex qui provoque un passage du mode utilisateur au mode noyau et un appel système, la synchronisation inter-core n'est pas pire.

0 votes

@DavidSchwartz Pourquoi ne pas partager votre implémentation de la version sans verrou ? Merci,

0 votes

Vous avez un while (true) dont les performances ne vous intéressent que lorsque vous quittez la boucle. Mais comme la boucle sera reprise plusieurs fois, la prédiction de branche du CPU prédit qu'elle sera reprise la dernière fois alors qu'elle ne l'est pas. Cela provoquera une branche mal prédite qui fera exploser les pipelines au moment où les performances sont les plus critiques. Quant à l'affichage de mon implémentation, il dépendra fortement de la plateforme -- il est impossible d'écrire un code sans verrou indépendant de la plateforme qui soit performant à cause de tous les problèmes que j'ai mentionnés ci-dessus. (Les verrous fonctionnent mieux pour le code portable).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X