更高级的技术可用于获取使用QThreadPool和QRunnable启动线程的执行结果
除了前面提到的方法,以下是一些更高级的技术可用于获取使用QThreadPool和QRunnable启动线程的执行结果:
1. 基于消息队列(Message Queue)的异步处理
- 设置消息队列:
-
可以利用第三方库(如ZeroMQ或RabbitMQ等)来设置消息队列系统。以ZeroMQ为例,首先需要在项目中引入ZeroMQ库并进行初始化。假设我们要创建一个简单的发布-订阅模式的消息队列,用于传递线程执行结果。
-
以下是一个简单的初始化示例(这里仅展示基本概念,实际应用可能需要更多配置):
-
#include <zmq.hpp>
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_PUB);
// 绑定到一个本地端口,用于发布消息(线程结果)
socket.bind("tcp://*:5555");
- 这里创建了一个ZeroMQ的上下文和一个发布类型的套接字,并将套接字绑定到本地的一个端口,以便后续发布线程执行结果。
- 在可运行对象类中发送消息到队列:
- 在继承自QRunnable的可运行对象类(如
MyRunnable
)的run
方法中,执行完任务后,将任务结果打包成合适的消息格式并发送到消息队列中。例如:
- 在继承自QRunnable的可运行对象类(如
class MyRunnable : public QRunnable {
public:
MyRunnable(int taskId) : m_taskId(taskId) {}
void run() override {
// 模拟执行任务,这里简单进行一个数学计算
int result = m_taskId * 2;
// 将结果打包成消息格式,这里简单使用字符串表示,实际可根据需要更复杂的格式
std::string message = std::to_string(m_taskId) + ":" + std::to_string(result);
// 发送消息到消息队列
zmq::message_t zmqMessage(message.size());
memcpy(zmqMessage.data(), message.c_str(), message.size());
socket.send(zmqMessage);
qDebug() << "线程 " << QThread::currentThreadId() << " 完成任务 " << m_taskId;
}
private:
int m_taskId;
};
- 在上述代码中,执行完任务得到结果后,将任务编号和结果组合成一个字符串消息,然后创建一个ZeroMQ消息对象并将字符串内容复制进去,最后通过套接字将消息发送到消息队列中。
- 在主线程中从队列接收消息获取结果:
- 在主线程中,创建一个ZeroMQ的接收套接字并连接到发布套接字绑定的端口,然后通过循环不断接收消息队列中的消息,解析出任务结果并进行处理。示例如下:
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThreadPool pool;
pool.setMaxThreadCount(3);
// 创建多个可运行对象实例并添加到线程池启动线程执行任务
for (int i = 1; i <= 5; ++i) {
MyRunnable *runnable = new MyRunnable(i);
pool.start(runnable);
}
// 创建接收套接字并连接到发布套接字绑定的端口
zmq::socket_t receiveSocket(context, ZMQ_SUB);
receiveSocket.connect("tcp://localhost:5555");
receiveSocket.setsockopt(ZMQ_SUBSCRIBE, "", 0);
// 从消息队列接收消息获取结果
while (true) {
zmq::message_t receivedMessage;
receiveSocket.recv(&receivedMessage);
// 解析消息获取任务结果
std::string receivedString(receivedMessage.data(), receivedMessage.size());
size_t colonPos = receivedString.find(":");
int taskId = std::stoi(receivedString.substr(0, colonPos));
int result = std::stoi(receivedString.substr(colonPos + 1));
qDebug() << "获取到任务 " << taskId << " 的结果:" << result;
// 可根据需要判断是否所有任务结果都已接收,若已接收则退出循环
if (taskId == 5) {
break;
}
}
qDebug() << "所有任务已完成,程序即将退出";
return a.exec();
}
- 在上述`main`函数中,创建了接收套接字并进行必要的设置后,通过循环接收消息队列中的消息,解析出任务编号和结果并进行打印,还可以根据具体情况判断是否所有任务结果都已接收,以便决定是否退出循环。
2. 利用函数式编程风格的异步操作(如使用C++ 的 std::async 和 std::future)结合智能指针
- 设置智能指针和任务函数:
- 首先,定义一个函数来执行具体的任务,这个函数将作为线程要执行的任务。例如:
#include <future>
#include <memory>
// 任务函数,模拟进行一个数学计算
int myTaskFunction(int taskId) {
return taskId * 2;
}
- 然后,我们可以利用`std::async`来异步启动这个任务,并将返回的`std::future`对象用智能指针(如`std::shared_ptr`)进行管理,以便更好地处理对象生命周期和资源管理。
- 在主线程中获取结果:
- 在主线程中,创建多个
std::async
调用并将它们的std::future
对象用智能指针保存起来。然后通过循环遍历这些智能指针,调用get
方法获取任务结果并进行处理。示例如下:
- 在主线程中,创建多个
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThreadPool pool;
pool.setMaxThreadCount(3);
// 创建多个用智能指针管理的std::async调用
std::vector<std::shared_ptr<std::future<int>>> futures;
for (int i = 1; i <= 5; ++i) {
std::shared_ptr<std::future<int>> future = std::make_shared<std::future<int>>(std::async(std::launch::async, myTaskFunction, i));
futures.push_back(future);
}
// 获取并处理结果
for (auto& future : futures) {
int result = future->get();
qDebug() << "获取到任务结果:" << result;
}
qDebug() << "所有任务已完成,程序即将退出";
return a.exec();
}
- 在上述`main`函数中,通过`std::async`异步启动任务并将返回的`std/ future`对象用智能指针保存,然后通过循环遍历这些智能指针,调用`get`方法获取任务结果并进行打印,从而实现了获取线程执行结果的目的。
3. 基于Actor模型的实现
- 定义Actor类:
- 首先创建一个类来表示Actor,在这个类中定义消息处理函数以及相关的状态变量等。例如:
#include <queue>
class Actor {
public:
Actor() {}
// 处理消息的函数
void handleMessage(const std::string& message) {
// 解析消息,假设消息格式为 "taskId:result"
size_t colonPos = message.find(":");
int taskId = std::stoi(message.substr(0, colonPos));
int result = std::stii(message.substr(colonPos + 1));
// 将结果保存到内部状态变量
m_results.push_back({taskId, result});
qDebug() << "Actor收到任务 " << taskId << " 的结果:" << result;
}
// 获取所有结果的函数
std::vector<std::pair<int, int>> getResults() {
return m_results;
}
private:
std::queue<std::pair<int, int>> m_results;
};
- 在上述代码中,`Actor`类有一个`handleMessage`函数用于处理接收到的消息(假设消息格式为`taskId:result`),解析出任务结果并保存到内部的队列中。还有一个`getResults`函数用于获取所有保存的结果。
- 在可运行对象类中发送消息给Actor:
- 在继承自QRunnable的可运行对象类(如
MyRunnable
)的run
方法中,执行完任务后,将任务结果按照与Actor约定的消息格式发送给Actor。例如:
- 在继承自QRunnable的可运行对象类(如
class MyRunnable : public QRunnable {
public:
MyRunnable(int taskId, Actor* actor) : m_taskId(taskId), m_actor(actor) {}
void run() override {
// 模拟执行任务,这里简单进行一个数学计算
int result = m_taskId * 2;
// 将结果打包成消息格式并发送给Actor
std::string message = std::to_string(m_taskId) + ":" + std::to_string(result);
m_actor->handleMessage(message);
qDebug() << "线程 " << QThread::currentThreadId() << " 完成任务 " << m_taskId;
}
private:
int m_taskId;
Actor* m_actor;
};
- 在上述代码中,执行完任务得到结果后,将任务编号和结果组合成一个字符串消息,然后发送给指定的Actor,由Actor进行处理和保存结果。
- 在主线程中获取结果:
- 在主线程中,创建Actor对象,并将其作为参数传递给每个
MyRunnable
对象,然后通过调用Actor的getResults
函数获取所有任务结果并进行处理。示例如下:
- 在主线程中,创建Actor对象,并将其作为参数传递给每个
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThreadPool pool;
pool.setMaxThreadCount(3);
// 创建Actor对象
Actor actor;
// 创建多个可运行对象实例并添加到线程池启动线程执行任务
for (int i = 1; i <= 5; ++i) {
MyRunnable *runnable = new MyRunnable(i, &actor);
pool.start(runnable);
}
// 获取并处理结果
std::vector<std::pair<int, int>> results = actor.getResults();
for (const auto& result : results) {
qDebug() << "获取到任务 " << result.first << " 的结果:" << result.second;
}
qDebug() << "所有任务已完成,程序即将退出";
return a.exec();
}
- 在上述`main`函数中,创建了Actor对象并将其作为参数传递给每个`Runnable`对象,在所有任务完成后,通过调用Actor的`getResults`函数获取所有任务结果并进行打印,从而实现了获取线程执行结果的目的。
这些高级技术在不同的场景下各有优劣,可以根据具体的项目需求、性能要求和开发团队的熟悉程度等因素来选择合适的方法来获取线程执行结果。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步