这段代码是否正确?我在某人的博客中看到了这个代码,它说 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;
}
答案 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;
}