2016-04-10 1 views
-1

ce code est-il correct? Je vois ce code dans le blog de quelqu'un, il a dit que volatile est sûr dans l'environnement d'un seul client et un seul producteur. Je ne sais pas si c'est vraiment sécurisé.Les variables volatiles sont-elles sûres dans un thread client et un thread producteur?

le code est le suivant:

#include <iostream> 
#include <pthread.h> 


template<class QElmType> 
struct qnode 
{ 
    struct qnode *next; 
    QElmType data; 
}; 
template<class QElmType> 
class queue 
{ 
public: 
     queue() {init();} 
     ~queue() {destroy();} 

     bool init() 
     { 
       m_front = m_rear = new qnode<QElmType>; 
       if (!m_front) 
         return false; 
       m_front->next = 0; 
       return true; 
     } 
     void destroy() 
     { 
       while (m_front) 
       { 
         m_rear = m_front->next; 
         delete m_front; 
         m_front = m_rear; 
       } 
     } 
     bool push(QElmType e) 
     { 
       struct qnode<QElmType> *p = new qnode<QElmType>; 
       if (!p) 
         return false; 
       p->next = 0; 
       m_rear->next = p; 
       m_rear->data = e; 
       m_rear = p; 
       return true; 
     } 
     bool pop(QElmType *e) 
     { 
       if (m_front == m_rear) 
         return false; 


       struct qnode<QElmType> *p = m_front; 
       *e = p->data; 
       m_front = p->next; 
       delete p; 
       return true; 
     } 
private: 
    struct qnode<QElmType> * volatile m_front, * volatile m_rear; 
}; 


queue<int> g_q; 


void *thread1(void * l) 
{ 
     int i = 0; 
     while (1) 
     { 
       g_q.push(i); 
       i++; 
       usleep(::rand()%1000); 
     } 
     return 0; 
} 
void *thread2(void * l) 
{ 
     int i; 
     while (1) 
     { 
       if (g_q.pop(&i)) 
         std::cout << i << std::endl; 
       //else 
         //std::cout << "can not pop" << std::endl; 
       usleep(::rand()%1000); 
     } 
     return 0; 
} 


int main(int argc, char* argv[]) 
{ 
     pthread_t t1,t2; 
     pthread_create(&t1, 0, thread1, 0); 
     pthread_create(&t2, 0, thread2, 0); 
     char ch; 
     while (1) 
     { 
       std::cin >> ch; 
       if (ch == 'q') 
         break; 
     } 
     return 0; 
} 
+0

Voir http://isvolatileusefulwiththreads.com/. –

Répondre

2

volatile peut travailler dans circonstances limitées, mais pas la façon dont vous l'utilisez.


Vous avez également un bug ou deux et l'attribution d'un noeud fictif au cours d'initialisation complique les choses simplement:

bool 
push(QElmType e) 
{ 
    struct qnode <QElmType> *p = new qnode <QElmType>; 

    if (!p) 
     return false; 

    p->next = 0; 

    // BUG: _not_ thread safe because multiple things being updated 
    m_rear->next = p; 

    // BUG: you want to set p->data here 
    m_rear->data = e; 

    m_rear = p; 

    return true; 
} 

bool 
pop(QElmType *e) 
{ 

    if (m_front == m_rear) 
     return false; 

    struct qnode <QElmType> *p = m_front; 

    *e = p->data; 
    m_front = p->next; 
    delete p; 

    return true; 
} 

Voici le code nettoyé à l'aide d'une serrure. Note: Après y avoir réfléchi, si vous essayiez de faire une implémentation de "file d'attente en anneau", je l'ai [par inadvertance] simplifiée dans une liste non-circulaire. J'étais plus préoccupé par le verrouillage. Même avec la version originale, le verrou est toujours nécessaire

bool 
init() 
{ 

    m_front = m_rear = nullptr; 

    return true; 
} 

bool 
push(QElmType e) 
{ 
    struct qnode <QElmType> *p = new qnode <QElmType>; 

    if (! p) 
     return false; 

    p->next = 0; 
    p->data = e; 

    // with the lock, now the _multiple_ can be updated _atomically_ 
    lock(); 

    if (m_front == nullptr) 
     m_front = p; 

    if (m_rear != nullptr) 
     m_rear->next = p; 

    m_rear = p; 

    unlock(); 

    return true; 
} 

bool 
pop(QElmType *e) 
{ 
    bool valid; 

    lock(); 

    struct qnode <QElmType> *p = m_front; 

    valid = (p != nullptr); 

    if (valid) { 
     *e = p->data; 

     m_front = p->next; 
     if (p == m_rear) 
      m_rear = m_front; 

     delete p; 
    } 

    unlock(); 

    return valid; 
} 

Une simple, valide utilisation de volatile serait:

volatile int stopflg; 

void * 
thread1(void *l) 
{ 
    while (! stopflg) { 
     // ... 
    } 

    return 0; 
} 

void * 
thread2(void *l) 
{ 
    while (! stopflg) { 
     //... 
    } 

    return 0; 
} 

int 
main(int argc, char *argv[]) 
{ 
    pthread_t t1, 
    t2; 

    pthread_create(&t1, 0, thread1, 0); 
    pthread_create(&t2, 0, thread2, 0); 

    char ch; 

    while (1) { 
     std::cin >> ch; 
     if (ch == 'q') { 
      stopflg = 1; 
      break; 
     } 
    } 

    return 0; 
} 
4

n ° volatile ne garantit pas la sécurité multithread.

Notez le titre, et notez la source:

Volatile: Almost Useless for Multi-Threaded Programming

Il y a une idée répandue que le mot-clé volatile est bon pour la programmation multi-thread. J'ai vu des interfaces avec des qualificatifs volatils justifiés comme "il pourrait être utilisé pour la programmation multi-thread ". Je pensais utile jusqu'à ces dernières semaines, quand il est finalement apparu sur moi (ou si vous préférez, a traversé ma tête épaisse) ce volatile est presque inutile pour la programmation multi-thread. Je vais vous expliquer pourquoi vous devriez en brosser la majeure partie à partir de votre code multithread.

...

-1

volatile est une directive de compilateur, indiquant que la variable peut changer à tout moment. C'est pour empêcher le compilateur d'effectuer des optimisations qui entraîneraient des erreurs. Utile lors de l'écriture d'un code C pour du matériel mappé en mémoire - les E/S sur l'appareil peuvent modifier l'état de la variable.

Cela n'a vraiment rien à voir avec l'écriture codée multi-thread.

Why is volatile needed in C?

+0

'volatile' n'est pas une directive de compilation.Cela fait partie du langage de base. –

+0

Oui, il est: « https://www.arduino.cc/en/Reference/Volatile » « Déclarer une variable volatile est une directive au compilateur » Notez que je ne dis pas qu'il était une directive de préprocesseur. –