2015-12-19 10 views
6

Ho due thread, un thread dovrebbe ricevere ed elaborare le richieste da un altro. Il secondo è trasferire le richieste e ricevere una risposta in modo sincrono. Ho provato il seguente schema: coda della coppia (valore, promessa). Il primo thread crea una promessa e lo inserisce in coda sincrona e in attesa di un valore restituito da future.get()std :: future.get() a volte bloccato in os x

Il problema è che a volte il thread è bloccato su future.get(), ma quando sospendo l'esecuzione del programma e lo proseguo di nuovo funziona correttamente. questo stucks ha natura casuale.

FutureQueue.h

#ifndef FutureQueue_h 
#define FutureQueue_h 

#include <queue> 
#include <future> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 

template <typename T, typename R> 
class Work{ 
public: 
    Work(){ 

    } 
    Work(T value, std::promise<R>* promise){ 
     m_value = value; 
     m_p_promise = promise; 
    } 
    std::promise<R>* m_p_promise; 
    T m_value; 
public: 
    T getValue(){ 
     return m_value; 
    } 
    void setResult(R result){ 
     m_p_promise->set_value(result); 
    } 
}; 


template <typename T, typename R> 
class FutureQueue 
{ 
public: 
    Work<T,R> getWork(){ 
     auto p = pop(); 
     return Work<T,R>(p.first,p.second); 
    } 
    R execute(T value) 
    { 
     std::promise<R> promise = std::promise<R>(); 
     std::future<R> f = promise.get_future(); 
     auto p = std::pair<T, std::promise<R>*>(value, &promise); 
     push(p); 
     return f.get(); 
    } 
    private: 
    std::pair<T,std::promise<R>*> pop(){ 
     std::unique_lock<std::mutex> mlock(mutex_); 
     while (queue_.empty()) 
     { 
      cond_.wait(mlock); 
     } 
     auto item = queue_.front(); 
     queue_.pop(); 
     return item; 
    } 
    void push(const std::pair<T,std::promise<R>*>& item){ 
     std::unique_lock<std::mutex> mlock(mutex_); 
     queue_.push(item); 
     mlock.unlock(); 
     cond_.notify_one(); 
    } 
    std::queue<std::pair<T,std::promise<R>*>> queue_; 
    std::mutex mutex_; 
    std::condition_variable cond_; 
}; 

#endif 

main.cpp

#include <iostream> 
#include <thread> 

#include "FutureQueue.h" 

using namespace std; 

atomic<bool> quit; 
FutureQueue<int, int> mQueue; 

void consumer(){ 
    Work<int,int> work; 
    while(true){ 
     work = mQueue.getWork(); 
     if (quit){ 
      break; 
     } 
     int value = work.getValue()+100; 
     work.setResult(value); 
    } 
    work.setResult(0); 
} 

int main(int argc, const char * argv[]) { 
    quit = false; 
    thread thread(consumer); 
    // test 2 
    for (int i=0;i<100000;i++){ 
     int value = mQueue.execute(i); 
     cout << "input " << i <<" execute result " << value << endl; 
    } 
    quit = true; 
    mQueue.execute(-1); 
    thread.join(); 
    return 0; 
} 

Non so cosa è sbagliato con questo codice, forse si può suggerire una migliore soluzione. Grazie

UPDATE

Stück si verifica solo in OS X con la versione di Apple LLVM 6.0

Non v'è alcun problema sotto gcc su OS X e Linux e Visual Studio in Windows

+0

Il mio studio visivo non fallisce, potrebbe essere un problema di piattaforma? – mksteve

risposta

0

Ci sono due fili, A con

for (int i=0;i<100000;i++){ 
    int value = mQueue.execute(i); 
    cout << "input " << i <<" execute result " << value << endl; 
} 
quit = true; 
mQueue.execute(-1); 

B con

thread thread(consumer); 

vi aspettate B prima esecuzione, poi bloccato a causa

while (queue_.empty()) 
    { 
     cond_.wait(mlock); 
    } 

B continuerà aperta fino Una corsa il codice

cond_.notify_one(); 

solito sarà ok seguente. Ma se A prima divertente "cond.notify_one()", la B chiama "con_.wait (mlock)", la B sarà bloccata per sempre.

+0

Se 'A' viene eseguito per primo, la coda non sarà vuota, quindi l'attesa non si verificherà (e il mutex impedisce una gara lì). –

+0

Prima di un'esecuzione 'cond.notify_one()' chiama 'queue_.push (item)', e B prima di chiamare 'cond_.wait (mlock)' controlla 'queue_.empty()'. ho provato a cambiare 'cond_.wait (mlock);' a 'cond_.wait_for (mlock, std :: chrono :: milliseconds (1));' e il risultato è lo stesso – Geka

+0

se si cambia "while (queue_.empty()) "a" se "cosa accadrà, sarà ancora bloccato? –