web-dev-qa-db-fra.com

Bonne façon de mettre en pause et de reprendre un fil std ::

J'utilise un std::thread dans mon C++ code pour interroger constamment certaines données et les ajouter à un tampon. J'utilise un C++ lambda pour démarrer le fil comme ceci:

StartMyThread() {

    thread_running = true;
    the_thread = std::thread { [this] {
        while(thread_running) {
          GetData();
        }
    }};
}

thread_running est un atomic<bool> déclaré dans l'en-tête de classe. Voici ma fonction GetData:

GetData() {
    //Some heavy logic which needs to be executed in a worker thread
}

Ensuite, j'ai également une fonction StopMyThread où je mets thread_running à false pour qu'il sorte de la boucle while dans le lambda block.

StopMyThread() {
  thread_running = false;
  the_thread.join();
}

Ça marche bien. Le thread démarre et s'arrête sans planter.

Ce code C++ est utilisé sur iOS, Android, OS X et Windows. Mon interface utilisateur d'application a un bouton qui m'oblige à démarrer et arrêter le fil sur une pression de bouton; ce bouton peut être fréquemment utilisé dans certaines occasions. Je peux voir un délai d'une fraction de seconde dans l'interface utilisateur lors de l'arrêt ou du démarrage du thread.

Ma question est: En C++, est-ce une bonne façon de démarrer/arrêter un thread fréquemment? Je pense qu'avec cela logique Je crée à chaque fois un nouveau fil. Et si je comprends bien, la création d'un nouveau thread oblige le système d'exploitation à allouer beaucoup de nouvelles ressources qui peuvent être chronophages. Et je pense que c'est l'erreur que je fais. Comment puis-je éviter ça ?

Comment peut-on utiliser le même thread sans en allouer un nouveau à plusieurs reprises tout au long du cycle de vie de l'application, et simplement le lire/le mettre en pause si nécessaire?

8

Il s'agit de l'exemple classique d'utilisation d'une variable de condition. Vous attendez sur un mutex et notifiez un thread lorsqu'une certaine condition est remplie; de cette façon, vous n'avez pas besoin d'allouer un nouveau thread lorsque vous en avez besoin, mais ce n'est pas toujours une bonne chose, si vous souhaitez économiser de la mémoire. Une alternative serait une coroutine cédant à une autre coroutine lorsque des données sont nécessaires, ce qui est sans doute plus joli. Vous devez implémenter vous-même les coroutines ou utiliser une bibliothèque prête à l'emploi, telle que boost.coroutine.

Exemple

::std::condition_variable cv_;
::std::mutex m_;
bool data_is_ready_{};

StartMyThread()
{
  ::std::thread([this]
    {
      for (;;)
      {
        ::std::unique_lock<decltype(m_)> l(m_);
        cv.wait(l, [this]{ return data_is_ready_; });

        // do your stuff, m_ is locked
        data_is_ready_ = false;
      }
    }
  ).detach();
}

Prévenir:

{
  ::std::unique_lock<decltype(m_)> l(m_);

  data_is_ready_ = true;
}

cv_.notify_one();

Comme il est souvent plus rapide de libérer le verrou avant de le notifier, que vice-versa.

5
user1095108