2015-04-21 12 views
12

Supponiamo di avere una discussione che dovrebbe eseguire periodicamente qualche attività ma questo periodo è 6 volte ogni ora 12 volte ogni ora (ogni 5 minuti), ho visto spesso codice che controlla la discussione ciclo con un is_running bandiera che viene controllato ogni ciclo, in questo modo:Interruzione thread a lungo sonno

std::atomic<bool> is_running; 

void start() 
{ 
    is_running.store(true); 
    std::thread { thread_function }.detach(); 
} 

void stop() 
{ 
    is_running.store(false); 
} 

void thread_function() 
{ 
    using namespace std::literals; 
    while (is_running.load()) 
    { 
     // do some task... 
     std::this_thread::sleep_for(5min); 
    } 
} 

Ma se la funzione stop() viene chiamato, diciamo, 1 millisecondo dopo start() il filo sarebbe vivo per 299999 millisecondi supplementari fino risveglia , controlla la bandiera e muore.

La mia comprensione è corretta? Come evitare di mantenere vivo (ma dormendo) un thread che avrebbe dovuto essere terminato? Il mio approccio migliore fino ad ora è la seguente:

void thread_function() 
{ 
    using namespace std::literals; 
    while (is_running.load()) 
    { 
     // do some task... 
     for (unsigned int b = 0u, e = 1500u; is_running.load() && (b != e); ++b) 
     { 
      // 1500 * 200 = 300000ms = 5min 
      std::this_thread::sleep_for(200ms); 
     } 
    } 
} 

Esiste un modo meno sporco e più semplice per raggiungere questo obiettivo?

+1

_6 volte ogni ora (ogni 5 minuti) _, ben 12 volte ogni ora o ogni 10 minuti? :) –

+0

@AlexandreLavoie che fallire! Grazie, lo correggerò! :) –

+3

http://en.cppreference.com/w/cpp/thread/condition_variable, vedere la prima frase qui. Invece di dormire per un periodo di tempo prestabilito, si entra in uno stato di attesa attendibile per la quantità di tempo così gli altri thread possono ancora interrompersi – stijn

risposta

12

Utilizzare una variabile di condizione. Aspetti sulla variabile di condizione o 5 minuti che passano. Ricordarsi di verificare la presenza di wakeups spurie.

cppreference

non riesco a trovare un buon posto overflow dello stack su come utilizzare una variabile di condizione in un minuto o due di google ricerca. La parte più difficile è rendersi conto che lo wait può riattivarsi senza passare né 5 minuti né inviare un segnale. Il modo più pulito per gestirlo è utilizzare i metodi di attesa con un lambda che verifica che il risveglio sia "buono".

here è un codice di esempio su cppreference che utilizza wait_until con un lambda. (wait_for con una lambda equivale a wait_until con una lambda). L'ho leggermente modificato.

Ecco una versione:

struct timer_killer { 
    // returns false if killed: 
    template<class R, class P> 
    bool wait_for(std::chrono::duration<R,P> const& time) const { 
    std::unique_lock<std::mutex> lock(m); 
    return !cv.wait_for(lock, time, [&]{return terminate;}); 
    } 
    void kill() { 
    { 
     std::unique_lock<std::mutex> lock(m); 
     terminate=true; // should be modified inside mutex lock 
    } 
    cv.notify_all(); // it is safe, and *sometimes* optimal, to do this outside the lock 
    } 
    // I like to explicitly delete/default special member functions: 
    timer_killer() = default; 
    timer_killer(timer_killer&&)=delete; 
    timer_killer(timer_killer const&)=delete; 
    timer_killer& operator=(timer_killer&&)=delete; 
    timer_killer& operator=(timer_killer const&)=delete; 
private: 
    std::condition_variable cv; 
    mutable std::mutex m; 
    bool terminate = false; 
}; 

live example.

Si crea un timer_killer in un punto condiviso. I thread client possono wait_for(time). Se restituisce false, significa che sei stato ucciso prima che la tua attesa fosse completata.

Il thread di controllo chiama solo kill() e tutti gli utenti che effettuano il wait_for ricevono un ritorno false.

Nota che c'è qualche contesa (blocco del mutex), quindi questo non è adatto per thread infiniti (ma poche cose lo sono). Considerare l'utilizzo di un programma di pianificazione se è necessario disporre di un numero illimitato di attività eseguite con ritardi arbitrari anziché un thread completo per attività ripetuta ritardata - ogni thread reale è verso l'alto di un megabyte di spazio di indirizzi di sistema utilizzato (solo per lo stack) .

-1

sì, con i mezzi di std::mutex, std::lock_guard e std::conditional_variable:

std::mutex mtx; 
std::conditional_variable cv; 

void someThreadFunction(){ 
    while(!stopThreadFlag){ 
    std::lock_gurad<std::mutex> lg(mtx); 
    cv.wait_for(lg,SOME_ITERVAL,!stopThreadFlag || wakeTheThreadVariable); 
    //the rest here 
    } 
} 
+0

Il tuo codice non gestisce le scia spurie. – Yakk

+1

Questo non verrà compilato. – Barry

+0

hai incluso tutti i capi giusti? –

2

Ci sono due modi tradizionali si potrebbe fare questo.

È possibile utilizzare un'attesa a tempo su una variabile di condizione e fare in modo che l'altro thread segnali il thread periodico per svegliarsi e morire quando è il momento.

In alternativa è possibile poll su un tubo con il sonno come un timeout anziché di dormire. Quindi scrivi un byte nella pipe e il thread si attiva e può uscire.

Problemi correlati