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