J'entre dans les threads C++ 11 et j'ai rencontré un problème.
Je veux déclarer une variable de thread comme globale et la démarrer plus tard.
Cependant, tous les exemples que j'ai vus semblent démarrer le fil immédiatement, par exemple
thread t(doSomething);
Ce que je veux c'est
thread t;
et démarrer le fil plus tard.
Ce que j'ai essayé c'est
if(!isThreadRunning)
{
thread t(readTable);
}
mais maintenant t est la portée du bloc. Je veux donc déclarer t puis démarrer le thread plus tard afin que t soit accessible aux autres fonctions.
Merci pour toute aide.
Le constructeur par défaut de std::thread
Instancie un std::thread
Sans démarrer ni représenter de thread réel.
std::thread t;
L'opérateur d'affectation déplace l'état d'un objet de thread et définit l'objet de thread attribué à son état initialisé par défaut:
t = std::thread(/* new thread code goes here */);
Cela crée d'abord un objet de thread temporaire représentant un nouveau thread, transfère la nouvelle représentation de thread dans l'objet de thread existant qui a un état par défaut et définit l'état de l'objet de thread temporaire sur l'état par défaut qui ne représente aucun thread en cours d'exécution. Ensuite, l'objet thread temporaire est détruit, sans rien faire.
Voici un exemple:
#include <iostream>
#include <thread>
void thread_func(const int i) {
std::cout << "hello from thread: " << i << std::endl;
}
int main() {
std::thread t;
std::cout << "t exists" << std::endl;
t = std::thread{ thread_func, 7 };
t.join();
std::cout << "done!" << std::endl;
}
Je donnerais au thread une variable de condition et un booléen appelé startRunning (initialement défini sur false). En fait, vous démarrez le thread immédiatement après sa création, mais la première chose qu'il fait est de se suspendre (en utilisant la variable_condition) et de ne commencer à traiter sa tâche réelle que lorsque la variable_condition est signalée de l'extérieur (et le startRunning = indicateur défini sur true).
MODIFIER: CODE PSEUDO:
// in your worker thread
{
lock_guard l( theMutex );
while ( ! startRunning )
{
cond_var.wait( l );
}
}
// now start processing task
// in your main thread (after creating the worker thread)
{
lock_guard l( theMutex );
startRunning = true;
cond_var.signal_one();
}
EDIT # 2: Dans le code ci-dessus, les variables theMutex, startRunning et cond_var doivent être accessibles par les deux threads. Que vous y arriviez en les rendant globales ou en les encapsulant dans une instance de structure/classe, cela dépend de vous.
Il n'y a pas de "standard" de création d'un thread "suspendu" qui je suppose est ce que vous vouliez faire avec la bibliothèque de threads C++. Parce qu'il n'est pas pris en charge sur toutes les plates-formes qui ont des threads, il n'est pas là dans l'API C++.
Vous souhaiterez peut-être créer une classe avec toutes les données nécessaires, mais pas réellement exécuter votre fonction de thread. Ce n'est pas la même chose que la création du fil de discussion mais peut être ce que vous voulez. Si c'est le cas, créez cela, puis liez plus tard l'objet et sa fonction operator()
ou start()
ou autre chose au thread.
Vous voudrez peut-être l'ID de thread pour votre thread. Cela signifie que vous devez réellement démarrer la fonction de thread. Cependant, il peut commencer par attendre une variable de condition. Vous signalez ou diffusez ensuite cette variable de condition ultérieurement lorsque vous souhaitez qu'elle continue à s'exécuter. Bien sûr, vous pouvez demander à la fonction de vérifier une condition après sa reprise au cas où vous auriez décidé de la fermer et de ne pas l'exécuter après tout (auquel cas elle reviendra instantanément).
Vous voudrez peut-être un std::thread
objet sans fonction. Vous pouvez le faire et l'attacher à une fonction ultérieurement à run
cette fonction dans un nouveau thread.
Vous pouvez utiliser motif singleton . Ou je dirais plutôt antipattern .
Dans un singleton, vous auriez std::thread
objet encapsulé. Lors du premier accès à singleton, votre thread sera créé et démarré.
déclaré pour la première fois dans la classe m_grabber
ne fait rien. Nous attribuons un objet de classe membre avec un nouveau avec la fonction lambda dans launch_grabber
La méthode et le thread avec lambda s'exécutent dans le contexte de classe source
.
class source {
...
std::thread m_grabber;
bool m_active;
...
}
bool source::launch_grabber() {
// start grabber
m_grabber = std::thread{
[&] () {
m_active = true;
while (true)
{
if(!m_active)
break;
// TODO: something in new thread
}
}
};
m_grabber.detach();
return true;
}
Comme le dit antred dans sa réponse , vous pouvez utiliser une variable de condition pour faire attendre le thread au début de sa routine.
Scott Meyers dans son livre "Effective Modern C++" (dans le "Item 39: Consider void
futures for one-shot event communication") propose d'utiliser void
- future au lieu d'entités de niveau inférieur ( drapeau booléen, variable conditionnelle et mutex). Le problème peut donc être résolu comme ceci:
auto thread_starter = std::promise<void>;
auto thread = std::thread([starter_future = thread_starter.get_future()]() mutable {
starter_future.wait(); //wait before starting actual work
…; //do actual work
});
…; //you can do something, thread is like “paused” here
thread_starter.set_value(); //“start” the thread (break its initial waiting)
Scott Meyers met également en garde contre les exceptions dans le deuxième …
(Marqué par le commentaire you can do something, thread is like “paused” here
). Si thread_starter.set_value()
n'est jamais appelé pour certaines raisons (par exemple, en raison d'exceptions lancées dans le second …
), Le thread attendra indéfiniment et toute tentative de le rejoindre entraînerait un blocage.
Comme les deux façons (basées sur condvar et basées sur l'avenir) contiennent une insécurité cachée et que la première manière (basée sur condvar) a besoin de code passe-partout, je propose d'écrire une classe wrapper autour de std::thread
. Son interface doit être similaire à celle de std::thread
(Sauf que ses instances doivent être assignables à partir d'autres instances de la même classe, pas à partir de std::thread
), Mais contenir des void start()
supplémentaires méthode.
class initially_suspended_thread {
std::promise<bool> starter;
std::thread impl;
public:
template<class F, class ...Args>
explicit initially_suspended_thread(F &&f, Args &&...args):
starter(),
impl([
starter_future = starter.get_future(),
routine = std::bind(std::forward<F>(f), std::forward<Args>(args)...)
]() mutable {if (starter_future.get()) routine();})
{}
void start() {starter.set_value(true);}
~initially_suspended_thread() {
try {starter.set_value(false);}
catch (const std::future_error &exc) {
if (exc.code() != std::future_errc::promise_already_satisfied) throw;
return; //already “started”, no need to do anything
}
impl.join(); //auto-join not-yet-“started” threads
}
…; //other methods, trivial
};
class initially_suspended_thread {
std::mutex state_mutex;
enum {INITIAL, STARTED, ABORTED} state;
std::condition_variable state_condvar;
std::thread impl;
public:
template<class F, class ...Args>
explicit initially_suspended_thread(F &&f, Args &&...args):
state_mutex(), state(INITIAL), state_condvar(),
impl([
&state_mutex = state_mutex, &state = state, &state_condvar = state_condvar,
routine = std::bind(std::forward<F>(f), std::forward<Args>(args)...)
]() {
{
std::unique_lock state_mutex_lock(state_mutex);
state_condvar.wait(
state_mutex_lock,
[&state]() {return state != INITIAL;}
);
}
if (state == STARTED) routine();
})
{}
void start() {
{
std::lock_guard state_mutex_lock(state_mutex);
state = STARTED;
}
state_condvar.notify_one();
}
~initially_suspended_thread() {
{
std::lock_guard state_mutex_lock(state_mutex);
if (state == STARTED) return; //already “started”, no need to do anything
state = ABORTED;
}
impl.join(); //auto-join not-yet-“started” threads
}
…; //other methods, trivial
};