我有一個非常簡單的隊列實現包裝一個固定的數組。它包含窺視,入隊和出隊。如果peek返回一個引用,我發現它最終會返回衝突的結果(衝突的結果意味着它將返回2個不同的值,沒有任何干預的出隊或排隊)。顯然,如果這個引用被保留和修改,就會發生這種情況,但據我所知,事實並非如此。事實上,再次打電話給出了預期的結果。線程安全隊列是不是線程安全的,如果peek返回一個引用,即使從未使用引用(與彙編!)
下面是Windows線程和互斥鎖的代碼。我也嘗試過在Linux上使用pthreads,結果相同。我顯然不明白一些東西......我拋棄了可執行文件,發現返回引用或值之間的唯一區別是當內存位置被解除引用時。例如:
如果返回一個參考,偷看包含:
lea eax,[edx+ecx*4+8]
然後在消費者線程:
cmp dword ptr [eax],1
但是,如果返回值,偷看包含:
mov eax,dword ptr [edx+ecx*4+8]
然後在消費者線程中:
cmp eax,1
謝謝!
#include <iostream>
#include <windows.h>
typedef void *(thread_func_type)(void *);
void start_thread(HANDLE &thread, thread_func_type *thread_func, void *arg)
{
DWORD id;
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, arg, 0, &id);
if (thread == NULL) {
std::cerr << "ERROR: failed to create thread\n";
::exit(1);
}
}
void join_thread(HANDLE &thread)
{
WaitForSingleObject(thread, INFINITE);
}
class ScopedMutex
{
HANDLE &mutex;
public:
ScopedMutex(HANDLE &mutex_) : mutex(mutex_)
{
WORD result = WaitForSingleObject(mutex, INFINITE);
if (result != WAIT_OBJECT_0) {
std::cerr << "ERROR: failed to lock mutex\n";
::exit(1);
}
};
~ScopedMutex()
{
ReleaseMutex(mutex);
};
};
template <typename T, unsigned depth>
class Queue
{
unsigned head, tail;
bool full;
T data[depth];
HANDLE mutex;
public:
Queue() : head(0), tail(0), full(false)
{
mutex = CreateMutex(NULL, 0, NULL);
if (mutex == NULL) {
std::cerr << "ERROR: could not create mutex.\n";
::exit(1);
}
};
T &peek()
{
while (true) {
{
ScopedMutex local_lock(mutex);
if (full || (head != tail))
return data[tail];
}
Sleep(0);
}
};
void enqueue(const T &t)
{
while (true) {
{
ScopedMutex local_lock(mutex);
if (!full) {
data[head++] = t;
head %= depth;
full = (head == tail);
return;
}
}
Sleep(0);
}
};
void dequeue()
{
while (true) {
{
ScopedMutex local_lock(mutex);
if (full || (head != tail)) {
++tail;
tail %= depth;
full = false;
return;
}
}
Sleep(0);
}
};
};
template <unsigned num_vals, int val, unsigned depth>
void *
producer(void *arg)
{
Queue<int, depth> &queue = *static_cast<Queue<int, depth> *>(arg);
for (unsigned i = 0; i < num_vals; ++i) {
queue.enqueue(val);
}
std::cerr << "producer " << val << " exiting.\n";
return NULL;
}
template <unsigned num_vals, int val, unsigned depth>
void *
consumer(void *arg)
{
Queue<int, depth> &queue = *static_cast<Queue<int, depth> *>(arg);
for (unsigned i = 0; i < num_vals; ++i) {
while (queue.peek() != val)
Sleep(0);
if (queue.peek() != val) {
std::cerr << "ERROR: (" << val << ", " << queue.peek() << ")" << std::endl;
std::cerr << "But peeking again gives the right value " << queue.peek() << std::endl;
::exit(1);
}
queue.dequeue();
}
return NULL;
}
int
main(int argc, char *argv[])
{
const unsigned depth = 10;
const unsigned num_vals = 100000;
Queue<int, depth> queue;
HANDLE p1, p2, c1, c2;
start_thread(p1, producer<num_vals, 1, depth>, &queue);
start_thread(p2, producer<num_vals, 2, depth>, &queue);
start_thread(c1, consumer<num_vals, 1, depth>, &queue);
start_thread(c2, consumer<num_vals, 2, depth>, &queue);
join_thread(p1);
join_thread(p2);
join_thread(c1);
join_thread(c2);
}
「衝突結果」是什麼意思? – 2010-02-22 23:30:22
當您的消費者和生產者函數將您的arg轉換爲Queue <>時,您可能想要執行reinterpret_cast而不是static_cast。 – thebretness 2010-02-23 00:19:54
我很困惑......你有沒有while循環作爲例子,還是他們真的在你的入隊和出隊函數中?你不應該需要那裏的while循環,排隊和出隊應該在被調用時添加和刪除一個項目......目前他們一直在做這件事。如果循環只是一個例子,那麼請忽略我的評論。 – Kiril 2010-02-23 00:22:32