在一个客户线程和一个生产者线程中,volatile变量是安全的吗?

时间:2016-04-10 05:12:26

标签: c++ c multithreading volatile

这段代码是否正确?我在某人的博客中看到了这个代码,它说 volatile 在只有一个客户和一个生产者的环境中是安全的。我不知道它是否真的是线程安全的。

代码如下:

#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;
}

2 个答案:

答案 0 :(得分:4)

没有。 volatile不保证多线程安全。

注意标题,并注意来源:

Volatile: Almost Useless for Multi-Threaded Programming

  

有一个普遍的概念,即关键字volatile是有益的   多线程编程。我已经看到了与volatile的接口   限定词称为“它可能用于多线程   编程“。我认为直到最近几周才有用   终于明白我(或者如果你愿意的话,穿过我的厚脸)   volatile对多线程编程几乎没用。生病   在这里解释为什么你应该从多线程中擦除大部分内容   代码。

     

...

答案 1 :(得分:2)

volatile可以在有限的环境中工作,但您使用它的方式。

你也有一两个bug在init期间分配一个虚拟节点只会使事情变得复杂:

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;
}

这是使用锁的清理代码。 注意:在考虑之后,如果您尝试执行“环队列”实现,我[无意中]将其简化为非循环列表。我更关心锁定。即使使用原始版本,仍然需要锁定

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;
}

使用volatile的简单有效将是:

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;
}