线程池-局部使用的风险

简介

ExecutorService执行任务,使用一个或多个池中的线程(使用Executors的工厂方法正常配置)。线程池解决两个问题:1.为大量的异步任务执行提高性能,提供了管理资源的一种方式(线程、执行任务的集合)。每一个ThreadPoolExecutor仍然提供了一些基础统计(完成任务的数量)。
编程人员通过Executors的newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor来获取最简单的线程池。
当一个新task通过execute方法提交后,线程池中核心线程数小于corePoolSize,一个新的线程将会创建,即使其它的工作线程是空闲的。如果线程数大于corePoolSize,小于maximumPoolSize,只有队列满的才会创建新的线程。
有三种通用的队列策略:直接处理SynchronousQueue,无界队列:LinkedBlockingQueue,有界队列:ArrayBlockingQueue

局部方法使用的风险

  1. 如果没有设置核心线程数,比如:newCachedThreadPool,在线程池的线程空闲时间到达60s后,线程会关闭,所有线程关闭后线程池也相应关闭回收。
  2. 如果设置的核心线程数,比如:newSingleThreadExecutor和newFixedThreadPool,如果没有主动关闭,或者设置核心线程的超时时间,核心线程会一直存在不会被关闭,这个线程池就不会被释放回收。线程池无法被回收,是因为线程池的引用被它的内部类Worker持有了,而Worker和线程一一对应,是对Thread的增强,所以本质上就是因为线程没有被释放。要执行线程退出需要两种情况:
    • 线程池的状态->stop,需要调用shutdown或者shutdownnow方法
    • getTask获取到空任务。有以下几种情况:当前线程数大于核心线程,会调用poll,超时后返回空任务;当前线程数小于等于核心线程,并且调用了allowCoreThreadTimeOut方法允许核心线程超时关闭的情况下,也是调用poll,超时后返回空任务。其他情况,调用take阻塞等待。正是调用了take阻塞导致核心线程迟迟不被关闭回收。

成员变量

    private final BlockingQueue<Runnable> workQueue;
    private final ReentrantLock mainLock = new ReentrantLock();
    private final HashSet<Worker> workers = new HashSet<Worker>();
    private final Condition termination = mainLock.newCondition();
    private int largestPoolSize;
    private long completedTaskCount;
    private volatile ThreadFactory threadFactory;
    private volatile RejectedExecutionHandler handler;
    private volatile long keepAliveTime;
    private volatile boolean allowCoreThreadTimeOut;
    private volatile int corePoolSize;
    private volatile int maximumPoolSize;
    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

线程提交

Worker和Task的区别,Worker是当前线程池中的线程,而task虽然是runnable,但是并没有真正执行,只是被Worker调用了run方法,后面会看到这部分的实现。 maximumPoolSize和corePoolSize的区别:这个概念很重要,maximumPoolSize为线程池最大容量,也就是说线程池最多能起多少Worker。corePoolSize是核心线程池的大小,当corePoolSize满了时,同时workQueue full(ArrayBolckQueue是可能满的)。 那么此时允许新建Worker去处理workQueue中的Task,但是不能超过maximumPoolSize。超过corePoolSize之外的线程会在空闲超时后终止。

### QNetworkAccessManager与线程池使用方法 #### 一、QNetworkAccessManager简介 `QNetworkAccessManager` 是 Qt 提供的一个用于执行网络操作的核心类。它支持 HTTP 请求(GET/POST)、下载文件等功能,并通过异步方式返回数据[^1]。为了提高效率并避免阻塞主线程,通常会将其与多线程机制结合使用。 #### 二、线程池的概念及其优势 线程池是一种设计模式,旨在减少频繁创建和销毁线程带来的开销。在 Qt 中可以利用 `QThreadPool` 和自定义任务来实现高效的并发处理[^2]。这种方式特别适合于需要发起大量短时间运行的任务场景,比如多个独立的网络请求。 #### 三、结合QThread/QThreadPool使用的推荐实践 虽然可以直接在一个单独的 `QThread` 实例中初始化 `QNetworkAccessManager` 来完成单一线程内的网络访问[^3],但如果涉及更复杂的并发需求,则建议采用基于 `QRunnable` 的线程池方案: ```cpp class NetworkTask : public QObject, public QRunnable { Q_OBJECT public: explicit NetworkTask(const QUrl &url) : url(url) {} protected: void run() override { QEventLoop loop; QNetworkAccessManager manager; QNetworkRequest request(url); auto reply = manager.get(request); connect(reply, &QIODevice::readyRead, [&]() { qDebug() << "Data received:" << reply->readAll(); }); connect(reply, &QNetworkReply::finished, [&]() { loop.quit(); }); loop.exec(); // Start local event processing. } private: QUrl url; }; ``` 上述代码片段展示了如何封装一个继承自 `QRunnable` 的任务对象,在其内部启动一个新的事件循环以等待网络响应结束后再退出当前线程[^5]。 当有新的请求到达时,只需简单地将该任务提交给全局或者局部维护好的线程池即可: ```cpp void enqueueHttpRequest(QThreadPool *pool, const QString &address){ QUrl target(address); pool->start(new NetworkTask(target)); } ``` 这样不仅能够充分利用系统的资源提升吞吐量,而且由于重用了已存在的工作线程而不是每次都重新构建新实体从而降低了整体消耗[^4]。 #### 四、注意事项 - **信号槽连接**:如果发现某些情况下信号槽不起作用,请确认是否正确设置了上下文关系以及调用形式(例如显式指定 `Qt::QueuedConnection` 或者 `Qt::BlockingQueuedConnection`)。 - **内存管理**:考虑到每个上传下载过程可能持续较长时间甚至失败中途取消等情况,合理规划生命周期非常重要;可以通过智能指针如 `std::shared_ptr<>` 自动释放不再被引用的对象实例防止泄露风险发生。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值