std::thread 使用 notify_one() 函数时即使有谓词也不会唤醒
std::thread not waking up when using notify_one() function even with predicate
我一直在尝试使用 Windows API 在串行端口上写入周期性和非周期性消息。我的代码架构如下:
主线程启动如下:
一个“消息发送线程”,负责通过串行端口写入数据。它将等待条件变量被其他线程唤醒,然后通过串行端口发送包含在缓冲区中的数据。一个标志保护我免受虚假唤醒。
构建消息的线程,填充数据缓冲区并每隔100ms用notify_one()函数通知“消息发送线程”
出于调试目的,主线程还在另一个构建消息的函数上循环了几次,并以与计时器每 500 毫秒相同的方式通知“消息发送线程”。这是代码
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <Windows.h>
std::condition_variable m_cvSend;
std::mutex m_mtxSend;
std::thread m_threadSendMessage;
std::thread m_threadPeriodicMessage;
char messageToSend[512] = { 0 };
bool newMessage = false;
bool threadRunning = false;
int count = 0;
void SendPeriodicMessage();
void SendFoo();
void sendMessageRun(void);
int main()
{
threadRunning = true;
//Start message sending thread
m_threadSendMessage = std::thread(&sendMessageRun);
m_threadPeriodicMessage = std::thread(&SendPeriodicMessage);
Sleep(1000);
while (count < 20) {
SendFoo();
Sleep(500);
count++;
}
m_threadSendMessage.join();
}
void sendMessageRun(void) {
std::unique_lock<std::mutex> lck(m_mtxSend);
DWORD bytesEcrits = 0;
while (threadRunning == true) {
m_cvSend.wait(lck, [&] {return newMessage; });
std::cout << "I'm HERE" << std::endl;
std::cout << messageToSend << std::endl;
//Send message over Serial port
//NOT RELEVANT FOR THE ISSUE
//Clean buffer
Sleep(20);
memset(messageToSend, 0, sizeof(messageToSend));
bytesEcrits = 0;
newMessage = 0;
}
}
//Envoi d'une consigne de pointage
void SendPeriodicMessage() {
while (true) {
//Send every 100 ms
Sleep(100);
std::lock_guard<std::mutex> lkEnvoi(m_mtxSend);
strcpy_s(messageToSend, "Thread 1");
//Flag for spurious wake ups
newMessage = true;
//// Debug
//std::cout << "Message to be sent periodically" << std::endl;
//std::cout << messageToSend << std::endl;
//End of critical section
//Notify sending thread
m_cvSend.notify_one();
}
}
void SendFoo() {
std::lock_guard<std::mutex> lkEnvoi(m_mtxSend);
char countChar[3] = { 0 };
_itoa_s(count, countChar, 10);
strcpy_s(messageToSend, "foo");
strcat_s(messageToSend, countChar);
//Flag for spurious wake ups
newMessage = true;
//End of critical section
//Notify sending thread
m_cvSend.notify_one();
}
虽然 运行 这个,我发现 foo 函数有时不会唤醒线程。其实函数应该是通过notify_one()函数来通知线程唤醒的。但是,我想 wait() 函数没有解锁,因为我没有在控制台上观察到另一个“我在这里”。
我已经看到用 notify_one() 唤醒一个线程是原子完成的,所以我不明白为什么它不会在干扰之间完成。
我尝试使用 Windows API 更改线程优先级,但它不起作用。
对于这第一个 post!
的一些帮助将不胜感激
谢谢!
您有两个可以“发送”消息的不同线程。这些线程是否已经有待处理的消息(例如 newMessage==true;
)。
notify_one
最终会通知接收线程,但不能保证它会立即通知。
在你的两个发送函数中添加一个 assert(! newMessage);
,你可能会看到一个被命中。
我一直在尝试使用 Windows API 在串行端口上写入周期性和非周期性消息。我的代码架构如下:
主线程启动如下:
一个“消息发送线程”,负责通过串行端口写入数据。它将等待条件变量被其他线程唤醒,然后通过串行端口发送包含在缓冲区中的数据。一个标志保护我免受虚假唤醒。
构建消息的线程,填充数据缓冲区并每隔100ms用notify_one()函数通知“消息发送线程”
出于调试目的,主线程还在另一个构建消息的函数上循环了几次,并以与计时器每 500 毫秒相同的方式通知“消息发送线程”。这是代码
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <Windows.h>
std::condition_variable m_cvSend;
std::mutex m_mtxSend;
std::thread m_threadSendMessage;
std::thread m_threadPeriodicMessage;
char messageToSend[512] = { 0 };
bool newMessage = false;
bool threadRunning = false;
int count = 0;
void SendPeriodicMessage();
void SendFoo();
void sendMessageRun(void);
int main()
{
threadRunning = true;
//Start message sending thread
m_threadSendMessage = std::thread(&sendMessageRun);
m_threadPeriodicMessage = std::thread(&SendPeriodicMessage);
Sleep(1000);
while (count < 20) {
SendFoo();
Sleep(500);
count++;
}
m_threadSendMessage.join();
}
void sendMessageRun(void) {
std::unique_lock<std::mutex> lck(m_mtxSend);
DWORD bytesEcrits = 0;
while (threadRunning == true) {
m_cvSend.wait(lck, [&] {return newMessage; });
std::cout << "I'm HERE" << std::endl;
std::cout << messageToSend << std::endl;
//Send message over Serial port
//NOT RELEVANT FOR THE ISSUE
//Clean buffer
Sleep(20);
memset(messageToSend, 0, sizeof(messageToSend));
bytesEcrits = 0;
newMessage = 0;
}
}
//Envoi d'une consigne de pointage
void SendPeriodicMessage() {
while (true) {
//Send every 100 ms
Sleep(100);
std::lock_guard<std::mutex> lkEnvoi(m_mtxSend);
strcpy_s(messageToSend, "Thread 1");
//Flag for spurious wake ups
newMessage = true;
//// Debug
//std::cout << "Message to be sent periodically" << std::endl;
//std::cout << messageToSend << std::endl;
//End of critical section
//Notify sending thread
m_cvSend.notify_one();
}
}
void SendFoo() {
std::lock_guard<std::mutex> lkEnvoi(m_mtxSend);
char countChar[3] = { 0 };
_itoa_s(count, countChar, 10);
strcpy_s(messageToSend, "foo");
strcat_s(messageToSend, countChar);
//Flag for spurious wake ups
newMessage = true;
//End of critical section
//Notify sending thread
m_cvSend.notify_one();
}
虽然 运行 这个,我发现 foo 函数有时不会唤醒线程。其实函数应该是通过notify_one()函数来通知线程唤醒的。但是,我想 wait() 函数没有解锁,因为我没有在控制台上观察到另一个“我在这里”。
我已经看到用 notify_one() 唤醒一个线程是原子完成的,所以我不明白为什么它不会在干扰之间完成。
我尝试使用 Windows API 更改线程优先级,但它不起作用。
对于这第一个 post!
的一些帮助将不胜感激谢谢!
您有两个可以“发送”消息的不同线程。这些线程是否已经有待处理的消息(例如 newMessage==true;
)。
notify_one
最终会通知接收线程,但不能保证它会立即通知。
在你的两个发送函数中添加一个 assert(! newMessage);
,你可能会看到一个被命中。