ThreadPool(16)

本文详细介绍了ThreadPool的设计与实现,包括成员变量的功能、构造与析构过程、启动与停止线程池的方法,以及任务分配与执行机制。此外,还提供了一个简单的测试案例。


threadPool类图

-----------------------

-mutex_:MutexLock  //互斥量 ---> mutexlock--->mutexlockGuard
-cond_:Codition //条件变量
-name:string    //名字
-threads_:boost::ptr_vector<muduo::Thread> //线程池容量
-queue_: std::deque<Task>    //任务队列,任务我们把他作为“函数”
-running_ :bool        // 线程池是否运行
-----------------------------
<<create>>-ThreadPool(name:string)
<<destroy>>-ThreadPool()
+start(numThreads:int):void// 启动线程池
+stop():void            //关闭线程池
+run(f:Task):void //    启动
+runInThread():void //线程池中的线程的回调函数
+take():Task         // 获取任务

--------------------------------------------------


ThreadPool头文件



// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#ifndef MUDUO_BASE_THREADPOOL_H
#define MUDUO_BASE_THREADPOOL_H

#include <muduo/base/Condition.h>
#include <muduo/base/Mutex.h>


#include <muduo/base/Thread.h>
#include <muduo/base/Types.h>

#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/ptr_container/ptr_vector.hpp>

#include <deque>

namespace muduo
{

class ThreadPool : boost::noncopyable //不可复制
{
 public:
  typedef boost::function<void ()> Task;

  explicit ThreadPool(const string& name = string());
  ~ThreadPool();

  void start(int numThreads);
  void stop();

  void run(const Task& f);

 private:
  void runInThread();
  Task take();

  MutexLock mutex_;
  Condition cond_;
  string name_;
  boost::ptr_vector<muduo::Thread> threads_;
  std::deque<Task> queue_; //线程池的任务队列
  bool running_;//线程池是否处于启动状态
};

}

#endif


ThreadPool的源文件


// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include <muduo/base/ThreadPool.h>

#include <muduo/base/Exception.h>

#include <boost/bind.hpp>
#include <assert.h>
#include <stdio.h>

using namespace muduo;

ThreadPool::ThreadPool(const string& name)
  : mutex_(), //初始化互斥量
    cond_(mutex_), //互斥量跟条件变量进行绑定
    name_(name),
    running_(false)
{
}
//线程池的虚构
ThreadPool::~ThreadPool()
{
    //如果线程池处于运行状态,关闭线程池里的线程
  if (running_)
  {
    stop();
  }
}
//1----->>启动线程池
void ThreadPool::start(int numThreads)
{
    //断言线程池是否为空
  assert(threads_.empty());
  //启动线程池
  running_ = true;
  //开辟numThreads个容量的线程池
  threads_.reserve(numThreads);
  //生产线程,这些线程可以说就是“消费者”
  for (int i = 0; i < numThreads; ++i)
  {
    char id[32];
    snprintf(id, sizeof id, "%d", i);
    threads_.push_back(new muduo::Thread(
        //线程池里的线程的回调函数  runInThread
          boost::bind(&ThreadPool::runInThread, this), name_+id));
    //启动线程
    threads_[i].start();
  }
}

/**
停止线程池里的线程
***/
void ThreadPool::stop()
{
  {
      //加锁
  MutexLockGuard lock(mutex_);
  running_ = false;
    //唤醒线程
  cond_.notifyAll(); 
  }
  //把线程加入退出队列
  for_each(threads_.begin(),
           threads_.end(),
           boost::bind(&muduo::Thread::join, _1));
}
//4---->>> 启动线程池
void ThreadPool::run(const Task& task)
{
    //如果线程池没有线程,那么直接执行任务,也就是说假设没有消费者,那么生产者直接消费产品.
    //而不把任务加入任务队列
  if (threads_.empty())
  {
    task();
  }
  //如果线程池有线程
  else
  {
      //加锁
    MutexLockGuard lock(mutex_);
        //加入任务队列
    queue_.push_back(task);
    //唤醒
    cond_.notify();
  }
}
//3----->>> 这是一个任务分配函数,线程池函数或者线程池里面的函数都可以到这里取出一个任务,然后在自己的线程中执行这和任务,返回一个任务指针
ThreadPool::Task ThreadPool::take()
{
  MutexLockGuard lock(mutex_);
  // always use a while-loop, due to spurious wakeup
  //防止假唤醒,running_ 用来接收线程池发送的“线程退出”命令
  while (queue_.empty() && running_)
  {
    cond_.wait();
  }
  Task task;
  if(!queue_.empty())
  {
    task = queue_.front();
    queue_.pop_front();
  }
  return task;
}

//2---->>线程池(可以把他当做主线程进行理解)里的线程的回调函数,可以在线程池函数中执行一些任务,并不是说只有线程池里面的函数才能执行任务,这能达到负载均衡的效果
void ThreadPool::runInThread()
{
  try
  {
        //测试线程池是否在运行 ,不运行马上停止线程池
    while (running_)
    {
      Task task(take()); //任务是函数,task == 函数指针
      if (task)
      {//执行任务,消费产品
        task();
      }
    }
  }
  catch (const Exception& ex)
  {
    fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
    fprintf(stderr, "reason: %s\n", ex.what());
    fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
    abort();
  }
  catch (const std::exception& ex)
  {
    fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
    fprintf(stderr, "reason: %s\n", ex.what());
    abort();
  }
  catch (...)
  {
    fprintf(stderr, "unknown exception caught in ThreadPool %s\n", name_.c_str());
    throw; // rethrow
  }
}


ThreadPool的测试程序


#include <muduo/base/ThreadPool.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/CurrentThread.h>

#include <boost/bind.hpp>
#include <stdio.h>

void print()
{
  printf("tid=%d\n", muduo::CurrentThread::tid());
}

void printString(const std::string& str)
{
  printf("tid=%d, str=%s\n", muduo::CurrentThread::tid(), str.c_str());
}

int main()
{
  muduo::ThreadPool pool("MainThreadPool");
  pool.start(5);

  pool.run(print);
  pool.run(print);
  for (int i = 0; i < 100; ++i)
  {
    char buf[32];
    snprintf(buf, sizeof buf, "task %d", i);
    pool.run(boost::bind(printString, std::string(buf)));
  }
 //CountDowndLatch 作文任务放进去,子线会执行,然后唤醒pool,pool就会调用stop停止线程池
  muduo::CountDownLatch latch(1);
  pool.run(boost::bind(&muduo::CountDownLatch::countDown, &latch));
  latch.wait();
  pool.stop();
}



<think>我们正在处理一个关于Qt线程池与Socket结合使用的问题。用户希望看到使用Qt的QThreadPool处理Socket任务的示例代码和教程。根据之前的引用,我们知道Qt提供了QThreadPool和QRunnable来实现线程池。同时,引用[1]展示了主函数中如何使用线程池启动多个Worker任务。引用[3]则展示了一个线程类的实现(继承QThread)。但是,用户特别提到要处理Socket任务。另外,引用[4]提到信号与槽的性能影响,这在处理Socket通信时也需要注意,因为频繁的信号发射可能会导致性能下降。因此,我们将创建一个基于QRunnable的自定义任务类,该类负责处理Socket通信。由于QRunnable不是QObject的子类,我们不能直接使用信号槽(除非混入QObject),但我们可以通过其他方式与主线程通信(例如使用QMetaObject::invokeMethod或通过线程安全的方式将结果传递给主线程)。不过,为了简化,我们可以让自定义的QRunnable任务类包含一个指向主线程对象的指针(或使用信号槽,但需要让任务类同时继承QRunnable和QObject,并添加Q_OBJECT宏)。但是注意:QRunnable本身不提供信号槽机制。有两种方案:1.创建一个继承自QObject和QRunnable的类,这样可以使用信号槽。但要注意多重继承的问题,并且确保在正确的线程中处理信号。2.使用事件传递结果(例如,使用QCoreApplication::postEvent)或使用线程安全的队列(如QQueue配合QMutex)来传递数据。由于用户请求的是教程和示例代码,我们将采用第一种方案,因为它更符合Qt的习惯,且易于理解。但需要注意,QRunnable的实例在线程池的线程中运行,因此我们在该对象中发射的信号会由接收者所在的线程的上下文处理(通过连接类型控制)。步骤:1.创建一个继承自QObject和QRunnable的类(如SocketTask)。2.在SocketTask中重写run()方法,在该方法中处理Socket操作。3.在SocketTask中定义信号(如resultReady),以便在任务完成时发送结果。4.主线程中创建SocketTask实例,并将其放入线程池。5.注意:由于SocketTask是在线程池中运行,所以它的run()方法在子线程中执行,因此其中不能直接访问主线程的GUI对象(如果需要更新界面,必须通过信号槽,并且注意连接方式)。但是,我们这里处理的是Socket通信,那么我们需要在run()中创建QTcpSocket或QUdpSocket。注意:Qt的套接字类需要事件循环,因此我们必须在run()方法中开启事件循环(使用exec())或者使用阻塞式的套接字操作。由于每个任务都是在一个单独的线程中运行,我们可以为每个任务创建一个QTcpSocket,并在该线程中使用它。但请注意,每个线程都需要有自己的套接字对象,并且需要在该线程中创建(即套接字对象不能跨线程使用)。因此,在run()函数中,我们将创建QTcpSocket对象,然后连接到服务器,进行读写操作。由于我们使用了事件驱动的套接字(非阻塞),我们需要在run()中开启一个局部的事件循环,直到完成操作。示例代码将实现一个简单的客户端任务,它连接到服务器,发送一条消息,然后等待响应。但注意:如果我们使用事件驱动,我们需要等待连接建立、数据发送和接收,这些都会涉及事件循环。因此,我们在run()函数中启动一个局部的事件循环。然而,为了避免复杂的嵌套事件循环,我们可以考虑使用阻塞套接字(使用waitForConnected等函数),但阻塞操作会一直占用线程直到操作完成,这可能不是高效的,尤其是在连接较慢的情况下。但在这个示例中,为了简单,我们将使用阻塞操作。这里我们选择使用阻塞操作,因为这样代码更简单,而且在这个任务中,我们只需要完成一次连接和一次数据收发。在实际应用中,如果需要处理多个连接或大量数据,可能需要非阻塞方式。下面是一个使用阻塞套接字的例子:注意:QRunnable对象在运行结束后会被线程池自动删除(需要设置setAutoDelete(true)),但是我们在创建时使用了new,所以如果不希望自动删除,可以设置为false,但这样就需要手动管理内存。在示例中,我们将设置自动删除。根据引用[1],主函数中使用globalInstance()获取全局线程池,然后启动任务,并等待所有任务完成。现在,我们开始编写示例代码。注意:以下代码仅作为示例,实际应用中需要考虑网络错误处理、超时等。</think> 以下是一个使用Qt线程池处理Socket任务的详细教程和示例代码,结合了QRunnable、QThreadPool和QTcpSocket实现高效的并发Socket处理。 ### 解决方案 通过QRunnable实现Socket任务,线程池管理线程资源,配合信号槽机制实现线程间通信。 #### 1. 自定义Socket任务类 (SocketTask.h) ```cpp #include <QRunnable> #include <QTcpSocket> #include <QObject> #include <QMutex> #include <QSemaphore> class SocketTask : public QObject, public QRunnable { Q_OBJECT public: explicit SocketTask(const QString& host, quint16 port, QObject* parent = nullptr) : QObject(parent), host(host), port(port) { setAutoDelete(true); // 任务完成后自动删除 } void run() override { QTcpSocket socket; socket.connectToHost(host, port); if (socket.waitForConnected(5000)) { // 发送数据 QByteArray data = "QUERY " + QByteArray::number(taskId) + "\n"; socket.write(data); if (!socket.waitForBytesWritten(3000)) { emit errorOccurred("Write timeout: " + socket.errorString()); return; } // 接收响应 if (socket.waitForReadyRead(5000)) { QByteArray response = socket.readAll(); emit resultReady(taskId, QString(response)); } else { emit errorOccurred("Read timeout: " + socket.errorString()); } } else { emit errorOccurred("Connection failed: " + socket.errorString()); } } signals: void resultReady(int taskId, const QString& data); void errorOccurred(const QString& message); private: QString host; quint16 port; static QAtomicInt taskCounter; int taskId = taskCounter++; }; ``` #### 2. 主线程控制器 (MainController.h) ```cpp #include <QObject> #include "SocketTask.h" class MainController : public QObject { Q_OBJECT public: explicit MainController(QObject* parent = nullptr) : QObject(parent) { threadPool.setMaxThreadCount(5); // 限制最大并发数 } void startTasks(int count) { const QString serverHost = "127.0.0.1"; const quint16 serverPort = 8888; for (int i = 0; i < count; ++i) { SocketTask* task = new SocketTask(serverHost, serverPort); connect(task, &SocketTask::resultReady, this, &MainController::handleResult); connect(task, &SocketTask::errorOccurred, this, &MainController::handleError); threadPool.start(task); } } signals: void taskCompleted(int count); private slots: void handleResult(int id, const QString& data) { qDebug() << "Task" << id << "result:" << data; completedCount++; if (completedCount == totalTasks) { emit taskCompleted(completedCount); } } void handleError(const QString& msg) { qWarning() << "Error:" << msg; } private: QThreadPool threadPool; int totalTasks = 0; int completedCount = 0; }; ``` #### 3. 服务器端示例 (TestServer.py) ```python # 用于测试的Python简易服务器 import socket, threading def handle_client(conn): while True: data = conn.recv(1024) if not data: break print(f"Received: {data.decode()}") conn.sendall(b"ACK " + data) conn.close() server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('0.0.0.0', 8888)) server.listen() print("Server started...") while True: conn, addr = server.accept() threading.Thread(target=handle_client, args=(conn,)).start() ``` ### 关键实现说明 1. **线程安全设计**: - 使用`QAtomicInt`生成唯一任务ID,避免资源竞争[^1] - 每个Socket在任务线程内单独创建,遵守Qt线程规则[^2] 2. **性能优化**: - `setMaxThreadCount()`控制最大并发数,避免资源过载 - `setAutoDelete(true)`自动清理已完成任务[^3] 3. **错误处理**: - 使用`waitForConnected()`等超时机制 - 通过errorOccurred信号统一报告错误 4. **跨线程通信**: - 信号槽自动切换线程上下文 - 使用QueuedConnection保证线程安全[^4] ### 使用流程 ```mermaid graph TD A[主线程] --> B[创建SocketTask] B --> C[线程池分配线程] C --> D[在子线程建立Socket连接] D --> E[执行通信任务] E --> F[通过信号返回结果] F --> G[主线程处理结果] ``` ### 注意事项 1. **Socket线程限制**: - Socket对象必须在创建它的线程内操作 - 避免跨线程访问Socket对象 2. **内存管理**: - 任务对象由线程池自动删除 - 信号接收者生命周期需长于任务 3. **性能调优**: - 根据硬件资源调整`MaxThreadCount` - 高并发场景建议使用`QSemaphore`限制资源访问[^4]
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值