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

Is volatile variables safe in one customer thread and one producer thread?

这段代码正确吗?我在某人的博客上看到这段代码,它说 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;
}

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

注意标题,并注明出处:

Volatile: Almost Useless for Multi-Threaded Programming

There is a widespread notion that the keyword volatile is good for multi-threaded programming. I've seen interfaces with volatile qualifiers justified as "it might be used for multi-threaded programming". I thought was useful until the last few weeks, when it finally dawned on me (or if you prefer, got through my thick head) that volatile is almost useless for multi-threaded programming. I'll explain here why you should scrub most of it from your multi-threaded code.

...

volatile 是一个编译指令,声明变量可以随时改变。这是为了防止编译器进行会导致错误的优化。在为内存映射的硬件编写 C 代码时很有用 - I/O 在设备上可以更改变量的状态。

真的跟写多线程代码没关系。

Why is volatile needed in C?

volatile 可以在 有限的 情况下工作,但 不是 您使用它的方式。


您还有一两个错误,在初始化期间分配虚拟节点只会使事情复杂化:

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

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

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