文章目录
0. 引言
本文实现一个简单的 Linux 消息传递机制,通过优先级队列、线程安全的消息队列和同步异步机制,模拟 Windows 中的 SendMessage
和 PostMessage
功能。
SendMessage
和 PostMessage
的主要区别在于:
SendMessage
是同步消息发送机制,调用线程会阻塞,直到目标线程处理完消息并返回结果。PostMessage
是异步消息发送机制,消息会立即被放入消息队列,调用线程不会等待消息处理的完成。
1. 设计思路
实现 SendMessage
和 PostMessage
的目标是让两个或多个线程能够通过消息传递进行通信,尤其是在一个线程中发送消息,另一个线程异步或同步地处理这些消息。设计要点:
-
消息结构的定义:设计一个通用的消息结构,包含消息的类型、参数和返回值。消息类型区分同步消息(
SendMessage
)和异步消息(PostMessage
)。 -
消息队列的使用:通过一个线程安全的消息队列(
MessageQueue
)来存储和管理消息。消息队列的线程安全性可以通过使用互斥锁(std::mutex
)和条件变量(std::condition_variable
)来实现。 -
同步与异步消息处理:通过
std::promise
和std::future
实现同步消息的返回值,而异步消息则直接丢入队列,无需返回结果。 -
优先级管理:为了确保同步消息的优先处理,我们可以使用
std::priority_queue
来存储消息,将同步消息的优先级设置为更高,从而确保SendMessage
被优先处理。 -
消息处理线程:独立的消息处理线程负责从队列中取出消息并进行处理。通过不断循环地从队列中取出消息,确保消息得到及时处理。
2. 完整代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#include <future>
#include <memory>
#include <chrono>
// 定义消息类型
enum class MessageType {
kSync, // 同步消息(SendMessage)
kAsync // 异步消息(PostMessage)
};
// 消息结构体
struct Message {
int msg_id;
int w_param;
int l_param;
std::shared_ptr<std::promise<int>> promise_ptr;
MessageType type;
// 用于优先级队列排序,确保同步消息优先处理
bool operator<(const Message& other) const {
return type > other.type; // 同步消息的优先级高
}
};
// 消息队列类,线程安全
class MessageQueue {
public:
// 添加消息到队列
void Enqueue(const Message& msg) {
std::lock_guard<std::mutex> lock(mtx_);
queue_.push(msg);
cv_.notify_one(); // 唤醒消息处理线程
}
// 从队列中取出消息
Message Dequeue() {
std::unique_lock<std::mutex> lock(mtx_);
cv_.wait(lock, [this] { return !queue_.empty() || terminate_flag_; });
if (terminate_flag_ && queue_.empty()) {
return Message{}; // 返回默认消息,标识终止
}
Message msg = queue_.top();
queue_.pop();
return msg;
}
// 终止消息处理
void Terminate() {
std::lock_guard<std::mutex> lock(mtx_);
terminate_flag_ = true;
cv_.notify_all(); // 通知消息处理线程终止
}
private:
std::priority_queue<Message> queue_;
std::mutex mtx_;
std::condition_variable cv_;
bool terminate_flag_ = false; // 用于通知终止消息处理
};
// 全局消息队列
MessageQueue g_message_queue;
// 消息处理线程函数
void MessageHandler() {
while (true) {
Message msg = g_message_queue.Dequeue();
if (msg.msg_id == 0 && !msg.promise_ptr) {
break; // 处理完终止消息,退出线程
}
// 处理消息(示例:简单输出消息ID及参数)
std::cout << "Processing Message ID: " << msg.msg_id
<< ", wParam: " << msg.w_param
<< ", lParam: " << msg.l_param << std::endl;
// 假设消息处理是返回 w_param + l_param 的结果
int result = msg.w_param + msg.l_param;
// 如果是同步消息,设置返回值
if (msg.promise_ptr) {
msg.promise_ptr->set_value(result);
}
}
}
// 启动消息处理线程
std::thread StartMessageLoop() {
return std::thread(MessageHandler);
}
// SendMessage 实现(同步消息)
int SendMessage(int msg_id, int w_param, int l_param) {
auto promise_ptr = std::make_shared<std::promise<int>>();
std::future<int> fut = promise_ptr->get_future();
Message msg;
msg.msg_id = msg_id;
msg.w_param = w_param;
msg.l_param = l_param;
msg.promise_ptr = promise_ptr;
msg.type = MessageType::kSync; // 设置为同步消息
g_message_queue.Enqueue(msg);
// 等待消息处理完成,并获取返回值
return fut.get();
}
// PostMessage 实现(异步消息)
void PostMessage(int msg_id, int w_param, int l_param) {
Message msg;
msg.msg_id = msg_id;
msg.w_param = w_param;
msg.l_param = l_param;
msg.promise_ptr = nullptr; // 异步消息不需要返回值
msg.type = MessageType::kAsync; // 设置为异步消息
g_message_queue.Enqueue(msg);
}
// 示例使用
int main() {
// 启动消息处理线程
std::thread msg_thread = StartMessageLoop();
// 发送一个同步消息
std::cout << "Sending synchronous message..." << std::endl;
int send_result = SendMessage(1, 10, 20);
std::cout << "SendMessage result: " << send_result << std::endl;
// 发送一个异步消息
std::cout << "Posting asynchronous message..." << std::endl;
PostMessage(2, 30, 40);
// 给异步消息一些处理时间
std::this_thread::sleep_for(std::chrono::seconds(1));
// 终止消息处理线程
g_message_queue.Terminate();
msg_thread.join();
return 0;
}
3. 关键设计细节
3.1. 消息结构 (Message
)
在消息传递机制中,首先需要定义消息的结构体。该结构体包含消息的 ID(msg_id
)、附加的参数(w_param
和 l_param
)、同步返回值的 promise_ptr
(针对 SendMessage
)以及消息类型(MessageType
)。
enum class MessageType {
kSync, // 同步消息(SendMessage)
kAsync // 异步消息(PostMessage)
};
struct Message {
int msg_id;
int w_param;
int l_param;
std::shared_ptr<std::promise<int>> promise_ptr;
MessageType type;
bool operator<(const Message& other) const {
return type > other.type; // 同步消息优先
}
};
在该结构体中:
msg_id
用于标识消息类型;w_param
和l_param
是附加的参数,用于传递消息相关的数据;promise_ptr
是一个指向std::promise<int>
的智能指针,SendMessage
通过它返回处理结果;type
用于标识消息类型,分为同步 (kSync
) 和异步 (kAsync
) 消息。
通过重载 <
运算符,我们可以确保同步消息(kSync
)在消息队列中具有较高的优先级,从而优先处理。
3.2. 消息队列 (MessageQueue
)
为了存储消息并确保线程安全,我们需要一个线程安全的队列。在这里我们使用 std::priority_queue
来存储消息。通过互斥锁 (std::mutex
) 来保证队列的线程安全,并使用条件变量 (std::condition_variable
) 实现线程的阻塞和唤醒。
class MessageQueue {
public:
void Enqueue(const Message& msg) {
std::lock_guard<std::mutex> lock(mtx_);
queue_.push(msg);
cv_.notify_one(); // 唤醒消息处理线程
}
Message Dequeue() {
std::unique_lock<std::mutex> lock(mtx_);
cv_.wait(lock, [this] { return !queue_.empty() || terminate_flag_; });
if (terminate_flag_ && queue_.empty()) {
return Message{}; // 终止信号
}
Message msg = queue_.top();
queue_.pop();
return msg;
}
void Terminate() {
std::lock_guard<std::mutex> lock(mtx_);
terminate_flag_ = true;
cv_.notify_all(); // 唤醒消息处理线程
}
private:
std::priority_queue<Message> queue_;
std::mutex mtx_;
std::condition_variable cv_;
bool terminate_flag_ = false;
};
在这个类中:
Enqueue
方法用于将消息添加到队列中,并唤醒等待的线程;Dequeue
方法用于从队列中获取消息,如果队列为空,则等待;Terminate
方法用于发送一个终止信号,通知消息处理线程退出。
3.3. 同步与异步消息
我们通过 std::promise
和 std::future
来实现同步消息的返回值,确保调用者能够获得处理结果。而对于异步消息,消息的发送线程不需要等待处理结果,直接将消息丢入队列即可。
// 同步消息(SendMessage)
int SendMessage(int msg_id, int w_param, int l_param) {
auto promise_ptr = std::make_shared<std::promise<int>>();
std::future<int> fut = promise_ptr->get_future();
Message msg;
msg.msg_id = msg_id;
msg.w_param = w_param;
msg.l_param = l_param;
msg.promise_ptr = promise_ptr;
msg.type = MessageType::kSync; // 同步消息
g_message_queue.Enqueue(msg);
// 等待处理结果并返回
return fut.get();
}
// 异步消息(PostMessage)
void PostMessage(int msg_id, int w_param, int l_param) {
Message msg;
msg.msg_id = msg_id;
msg.w_param = w_param;
msg.l_param = l_param;
msg.promise_ptr = nullptr; // 异步消息不需要返回值
msg.type = MessageType::kAsync; // 异步消息
g_message_queue.Enqueue(msg);
}
SendMessage
通过 std::promise
和 std::future
实现同步消息的等待和返回,而 PostMessage
则是简单地将消息放入队列,不需要等待结果。
3.4. 消息处理线程
消息处理线程负责从队列中取出消息并进行处理。在处理完消息后,如果是同步消息,设置返回值。
void MessageHandler() {
while (true) {
Message msg = g_message_queue.Dequeue();
if (msg.msg_id == 0 && !msg.promise_ptr) {
break; // 处理完终止消息,退出线程
}
// 处理消息
int result = msg.w_param + msg.l_param; // 示例处理逻辑
// 设置返回值(如果是同步消息)
if (msg.promise_ptr) {
msg.promise_ptr->set_value(result);
}
}
}
该线程不断从消息队列中取出消息并处理,示例中我们简单地返回 w_param + l_param
作为处理结果。如果是同步消息,处理结果会通过 std::promise
返回给调用者。