静态内存中的STL结构“丢失”跨线程数据

发布于 2024-08-23 12:14:12 字数 2028 浏览 1 评论 0原文

我正在使用 pthread 编写一个多线程演示程序,其中一个线程将数据加载到 STL 队列中,另一个线程从中读取数据。听起来很微不足道,对吧?不幸的是,推入队列的数据正在消失。我对多线程并不陌生,也不熟悉内存结构 - 然而,这让我感到困惑。

这些是我对队列本身和保护它的互斥体的声明,它们位于客户端代码包含的标头中:

static std::queue<int32_t> messageQueue;
static pthread_mutex_t messageQueueLock; 

当程序启动时,它使用进程共享属性初始化互斥体:

pthread_mutexattr_t sharedAttr;
pthread_mutexattr_init(&sharedAttr);
pthread_mutexattr_setpshared(&sharedAttr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&messageQueueLock, &sharedAttr);

然后启动“生产者”线程和“消费者”线程,并让它们做自己的事情。生产者线程将新项目推送到队列中,然后进入睡眠状态。以下是它向队列添加内容的行:

pthread_mutex_lock(&messageQueueLock);
messageQueue.push(message);
pthread_mutex_unlock(&messageQueueLock);

然后它会休眠并让消费者线程接管。然而,当消费者线程检查队列中的项目时,队列神奇地是空的。

我已经使用 gdb 逐步完成了该程序。下面是我运行的输出。您可以看到生产者在队列中添加了一些内容,我打印了队列的大小以确保它在那里,有一个到消费者线程的上下文切换,我再次打印了队列的大小,它是空的。检查一下:

(gdb) b main_ex.cpp:70
Breakpoint 1 at 0x100006a24: file main_ex.cpp, line 70.
(gdb) run
Starting program: a.out 
Reading symbols for shared libraries ++. done
Creating the mutex.
Producer thread starting up. 
PRODUCER: Creating a message to send.
PRODUCER: Adding the message to the queue.
[Switching to process 7432]

Breakpoint 1, yourProcess () at main_ex.cpp:70
70      pthread_mutex_lock(&messageQueueLock);
(gdb) n
71      messageQueue.push(message);
(gdb) p messageQueue.size()
$1 = 0
(gdb) n
72      pthread_mutex_unlock(&messageQueueLock);
(gdb) p messageQueue.size()
$2 = 1
(gdb) b consumer.cpp:81
Breakpoint 2 at 0x1000043f7: file consumer.cpp, line 81.
(gdb) c
Continuing.
PRODUCER: Sleep time!
[Switching to process 7432]

Breakpoint 2, Producer::processMessageQueue (this=0x1001000c0) at producer.cpp:81
81      pthread_mutex_lock(&messageQueueLock);
(gdb) n
83      if(messageQueue.empty()) {
(gdb) p messageQueue.size()
$3 = 0
(gdb) quit

所以,我真的不确定发生了什么。队列仅在关键部分(读/写)中被访问,队列是静态的,并且标头是 if-def'd 的,不会被多重包含。

我很感激任何人可以提供的帮助!

I'm writing a multi-threaded demo program using pthreads, where one thread loads data into an STL queue, and another thread reads from it. Sounds trivial, right? Unfortunately, data pushed into the queue is vanishing. I'm not new to multithreading, nor am I unfamiliar with memory structures - however, this has me stumped.

These are my declarations for the queue itself and the mutex that protects it, which are located in a header included by the client code:

static std::queue<int32_t> messageQueue;
static pthread_mutex_t messageQueueLock; 

When the program starts up, it initializes the mutex with the process shared attribute:

pthread_mutexattr_t sharedAttr;
pthread_mutexattr_init(&sharedAttr);
pthread_mutexattr_setpshared(&sharedAttr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&messageQueueLock, &sharedAttr);

It then launches the 'producer' thread and 'consumer' threads, and lets them do their thing. The producer thread pushes a new item onto the queue and then goes to sleep. Here are the lines where it adds something to the queue:

pthread_mutex_lock(&messageQueueLock);
messageQueue.push(message);
pthread_mutex_unlock(&messageQueueLock);

It then sleeps and lets the consumer thread take over. However, when the consumer thread checks for items in the queue, the queue is magically empty.

I have stepped through the program using gdb. Below is the output of my run. You can see where the producer adds something to the queue, I print the queue's size to make sure it's there, there is a context switch to the consumer thread, I print the queue's size again, and it's empty. Check it out:

(gdb) b main_ex.cpp:70
Breakpoint 1 at 0x100006a24: file main_ex.cpp, line 70.
(gdb) run
Starting program: a.out 
Reading symbols for shared libraries ++. done
Creating the mutex.
Producer thread starting up. 
PRODUCER: Creating a message to send.
PRODUCER: Adding the message to the queue.
[Switching to process 7432]

Breakpoint 1, yourProcess () at main_ex.cpp:70
70      pthread_mutex_lock(&messageQueueLock);
(gdb) n
71      messageQueue.push(message);
(gdb) p messageQueue.size()
$1 = 0
(gdb) n
72      pthread_mutex_unlock(&messageQueueLock);
(gdb) p messageQueue.size()
$2 = 1
(gdb) b consumer.cpp:81
Breakpoint 2 at 0x1000043f7: file consumer.cpp, line 81.
(gdb) c
Continuing.
PRODUCER: Sleep time!
[Switching to process 7432]

Breakpoint 2, Producer::processMessageQueue (this=0x1001000c0) at producer.cpp:81
81      pthread_mutex_lock(&messageQueueLock);
(gdb) n
83      if(messageQueue.empty()) {
(gdb) p messageQueue.size()
$3 = 0
(gdb) quit

So, I'm really not sure what is going on. The queue is only ever accessed in a critical section (both read/write), the queue is static, and the header is if-def'd to not be multi-included.

I'm grateful for any help anyone can offer!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

怀中猫帐中妖 2024-08-30 12:14:12

标头防护可防止每个翻译单元出现多次包含。然而,不同的翻译单位会重新包含它们。

就您而言,似乎他们每个人都拥有自己的静态队列和互斥体。另外,即使您是正确的,也要考虑一下:如果不重新包含标头,翻译单元将不知道队列和互斥体是什么!您会尝试使用一些未声明的标识符。

您需要 extern,这实际上与 static 相反:

extern std::queue<int32_t> messageQueue;
extern pthread_mutex_t messageQueueLock;

然后在一个单元中实际定义它们:

std::queue<int32_t> messageQueue;
pthread_mutex_t messageQueueLock;

Header guards protect from multiple inclusion per translation unit. However, different translation units will re-include them.

In your case, it seems they are each getting their very own static queue and mutex. Also, consider even if you were correct: Without re-including the header, the translation unit would have no idea what queue and mutex are! You'd be trying to use some undeclared identifier.

You need extern, which is actually the opposite of static:

extern std::queue<int32_t> messageQueue;
extern pthread_mutex_t messageQueueLock;

Then in one unit, actually define them:

std::queue<int32_t> messageQueue;
pthread_mutex_t messageQueueLock;
寻找一个思念的角度 2024-08-30 12:14:12

您应该验证两个线程是否实际上正在访问同一队列,如果不是,请尝试避免静态队列并在主函数或方便的地方创建它。

You should verify whether the two threads are actually accessing the same queue or not, if not try to avoid the static queue and create it for example in the main function or somewhere convenient.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文