2012-12-28 6 views
6

Sto giocando con alcuni socket, thread e mutex. La mia domanda riguarda fili e mutex:Lo sblocco di mutex fallisce in modo strano

int ConnectionHandler::addNewSocket(){ 

    this->connectionList_mutex.lock(); 
    std::cout << "test1" << std::endl; 
    this->connectionList_mutex.unlock(); 

    return 0; 
} 

int ConnectionHandler::main(){ 
    while(true){ 
     this->connectionList_mutex.lock(); 
     std::cout << "test2" << std::endl; 
     this->connectionList_mutex.unlock(); 
    } 

}` 

La funzione principale è in esecuzione in un thread, mentre l'addNewSocket viene chiamato da un altro thread. Il problema è che, quando addNewSocket viene chiamato una volta (dal secondo thread), lo sblocco successivo da parte di thread 1 (main) fallirà con uno strano "SIGABRT di segnale". Ho lavorato due giorni su questo ora, ma non sono riuscito a farlo riparare, purtroppo. Spero che puoi aiutarmi.

Modifica: ConnectionHandler è una classe che ha connectionList_mutex come membro.

Modifica: A volte viene visualizzato anche questo errore: "Asserzione non riuscita: (ec == 0), funzione sblocco, file /SourceCache/libcxx/libcxx-65.1/src/mutex.cpp, riga 44." ma si verifica casualmente.

Modifica: Questa è l'intera classe (Ridotta al minimo, dovrebbe essere indipendente dal contesto in una certa misura, ma si blocca quando la inserisco subito dopo un client collegato e funziona se la metto subito dopo l'avvio:

..
class ConnectionHandler{ 
public: 
    ConnectionHandler(); 
    int addNewSocket(); 
private: 
    int main(); 
    static void start(void * pThis); 

    std::mutex connectionList_mutex; 
}; 

ConnectionHandler::ConnectionHandler(){ 
    std::thread t(&this->start, this); 
    t.detach(); 
} 
void ConnectionHandler::start(void * pThis){ 
    ConnectionHandler *handlerThis; 
    handlerThis = (ConnectionHandler *)pThis; 
    handlerThis->main(); 
} 


int ConnectionHandler::addNewSocket(){ 

    this->connectionList_mutex.lock(); 
    std::cout << "test1" << std::endl; 
    this->connectionList_mutex.unlock(); 

    return 0; 
} 

int ConnectionHandler::main(){ 
    while(true){ 
     this->connectionList_mutex.lock(); 
     std::cout << "test2" << std::endl; 
     std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
     this->connectionList_mutex.unlock(); 

    } 

} 
+0

Perché tag std? Il tuo mutex std :: mutex o qualcosa del genere? –

+0

sì il mutex e il thread sono entrambi C++ 11 std – sh4kesbeer

+0

ok, forse sono stupido, ma ora (ho creato un piccolo codice) che funziona, devo solo capire, perché non funziona nel contesto del mio vero programma – sh4kesbeer

risposta

5

la mia ipotesi è che il vostro oggetto ConnectionHandler viene distrutto da qualche Inoltre, è stato definito ConnectionHandler::start in modo stupido

in primo luogo, ConnectionHandler::start dovrebbero essere definite in questo modo:

void ConnectionHandler::start(ConnectionHandler * pThis){ 
    pThis->main(); 
} 

La classe C++ 11 ::std::thread è perfettamente in grado di preservare il tipo dell'argomento della funzione, pertanto non è necessario ricorrere a void *.

In secondo luogo, aggiungere questo codice:

void ConnectionHandler::~ConnectionHandler(){ 
    const void * const meptr = this; 
    this->connectionList_mutex.lock(); 
    ::std::cout << "ConnectionHandler being destroyed at " << meptr << ::std::endl; 
    this->connectionList_mutex.unlock(); 
} 

e cambiare il costruttore di leggere:

ConnectionHandler::ConnectionHandler(){ 
    const void * const meptr = this; 
    ::std::cout << "ConnectionHandler being created at " << meptr << ::std::endl; 
    std::thread t(&this->start, this); 
    t.detach(); 
} 

Questo ti mostrerà quando l'oggetto ConnectionHandler viene distrutta. E la mia ipotesi è che il tuo codice lo stia distruggendo mentre il tuo thread separato è ancora in esecuzione.

La cosa meptr è perché operator << ha un sovraccarico per void * che stampa il valore del puntatore. La stampa del valore del puntatore per this consente di abbinare le chiamate al costruttore e al distruttore se si creano più oggetti ConnectionHandler.

Edit: Poiché si è scoperto che ero corretto, ecco come mi sento di raccomandare che si scrive la classe gioco ConnectionHandler:

#include <iostream> 
#include <atomic> 
#include <thread> 
#include <chrono> 
#include <mutex> 

class ConnectionHandler { 
public: 
    ConnectionHandler(); 
    ~ConnectionHandler(); 
    ConnectionHandler(const ConnectionHandler &) = delete; 
    const ConnectionHandler &operator =(const ConnectionHandler &) = delete; 

    int addNewSocket(); 

private: 
    int main(); 
    static void start(ConnectionHandler * pThis); 

    ::std::mutex connectionList_mutex; 
    volatile ::std::atomic_bool thread_shutdown; 
    ::std::thread thread; 
}; 

ConnectionHandler::ConnectionHandler() 
    : thread_shutdown(false), thread(&this->start, this) 
{ 
} 

ConnectionHandler::~ConnectionHandler() 
{ 
    thread_shutdown.store(true); 
    thread.join(); 
} 

void ConnectionHandler::start(ConnectionHandler * pThis){ 
    pThis->main(); 
} 

int ConnectionHandler::addNewSocket(){ 
    ::std::lock_guard< ::std::mutex> lock(connectionList_mutex); 
    ::std::cout << "test1" << ::std::endl; 

    return 0; 
} 

int ConnectionHandler::main(){ 
    while(!thread_shutdown.load()){ 
     ::std::lock_guard< ::std::mutex> lock(connectionList_mutex); 
     ::std::cout << "test2" << ::std::endl; 
     ::std::this_thread::sleep_for(::std::chrono::milliseconds(100)); 

    } 
    return 0; 
} 
+1

Primo: Grazie mille, ragazzi qui allo stackoverflow sono fantastici! Speriamo che un giorno avrò abbastanza abilità per aiutare le persone, come te. Secondo: ora che so che ConnectionHandler è stato distrutto subito dopo la sua creazione, sto iniziando a indagare su questo, senza il tuo gentile suggerimento probabilmente avrei appena rinunciato a questo progetto, perché ho cercato di correggere l'errore per una settimana :) – sh4kesbeer

+0

Ok, ho trovato l'errore: ho creato ConnectionHandler all'interno di una funzione e non appena la funzione è terminata l'istanza è stata distrutta. In connessione a questo [collegamento] (http://stackoverflow.com/questions/6403055/object-destruction-in-c) ho capito, che devo creare un puntatore che punta a un gestore creato con il "nuovo" "operator (ConnectionHandler * newHandler = new ConnectionHandler;). In questo caso (come non ho capito) il puntatore viene distrutto, ma l'istanza di ConnectionHandler rimane in memoria. Ancora una volta: grazie per il vostro gentile aiuto! – sh4kesbeer

+0

@ sh4kesbeer: è ancora probabilmente la soluzione sbagliata. È necessario pensare a quale thread 'possiede' l'oggetto ConnectionManager. In questo caso, l'unica vera scelta è il thread distaccato poiché quel thread ha bisogno dell'oggetto finché dura e non hai modo di chiuderlo. Questo significa che devi avere un modo per "chiedere" al thread staccato di distruggere il tuo oggetto e chiudersi. Creandolo con una sorta di "nuovo" tipo di opere, ma crea una perdita di memoria ed è nel complesso una soluzione disordinata. – Omnifarious