QT/C++ 多线程并发下载实践

在python线程池测试例子中,用到了queue的功能,python中,queue是阻塞式获取元素,所以是线程安全的,参考如下的示例:

python 复制代码
from concurrent.futures import ThreadPoolExecutor
from queue import Queue
import time

def worker(queue,id):
    while True:
        item = queue.get()
        if item is None:
            break
        print(f" thread {id} Processing {item}")
        # 模拟处理耗时
        time.sleep(0.5)
    print("Worker exiting")

q = Queue()
executor = ThreadPoolExecutor(max_workers=5)

future_list = []
for i in range(5):
    future = executor.submit(worker, q,i)
    future_list.append(future)

# 提交任务
#future = executor.submit(worker, q)
#future = executor.submit(worker, q)

# 生产数据
for i in range(15):
    q.put(i)
for i in range(15):
    q.put(None)  # 发送终止信号

for f in future_list:
    f.result()

executor.shutdown()

在Qt中,QQueue对象并不是线程安全的,所以我们需要通过改造来实现类似的功能。

一、Qt BlockingQueue定义

为了实现多线性并发,我们需要在队列的take方法中启用QMutexLocker来保证线程安全,并启动QWaitCondition的wait方法以及waitone方法来实现元素阻塞式的访问。

定义"blockingQueue.h":

cpp 复制代码
#include <QWaitCondition>
#include <QQueue>
#include <QMutex>

template <typename T>
class blockingQueue
{
public:
    blockingQueue() {}
    void put(const T& value)
    {
        QMutexLocker locker(&m_mutex);
        m_queue.enqueue(value);
        m_condition.wakeOne();   //唤醒等待队列中的一个线程(来自wait)
    }
    T take()
    {
        QMutexLocker locker(&m_mutex);
        //队列为空,则等待,否则直接返回队列头元素
        while (m_queue.isEmpty()) {
            m_condition.wait(&m_mutex);
        }
        return m_queue.dequeue();
    }
    bool isEmpty() const
    {
        QMutexLocker locker(&m_mutex);
        return m_queue.isEmpty();
    }
    int size() const
    {
        QMutexLocker locker(&m_mutex);
        return m_queue.size();
    }

private:
    QQueue<T> m_queue;
    mutable QMutex m_mutex;
    QWaitCondition m_condition;
};

二、下载任务类定义

该类中,定义下载项目和下载任务对象。并在后续的线程池中,作为参数传递给执行函数。

定义"downloadTask.h":

cpp 复制代码
//下载项目
struct downloadItem
{
	QString src;
	QString dest;
	bool quit;
	downloadItem(bool isQuit = false)
	{
		quit = isQuit;
	}
};

//整个下载任务,包含所有下载项目队列
class downloadTask  : public QObject
{
	Q_OBJECT

public:
	downloadTask(QObject *parent = nullptr);
	~downloadTask();
	bool isQuit() { QMutexLocker locker(&m_mutex);return m_quit; };
	void quit() { QMutexLocker locker(&m_mutex); m_quit = true; };
	downloadItem get() { return m_Queue.take(); };
	void add(downloadItem &item) {  m_Queue.put(item); };;
private:
	mutable QMutex m_mutex;
	bool m_quit = false;
	blockingQueue<downloadItem> m_Queue;
};

三、创建下载任务

以下代码模拟重建100个下载任务,并在正常任务后面添加终止标记

cpp 复制代码
void createTask()
{
    downloadTask* task = new downloadTask();
    //创建任务
    for (size_t i = 0; i < 100; i++)
    {
        downloadItem item;
        item.src = QString("%1.jpg").arg(i);
        item.dest = QString("%1-d.jpg").arg(i);
        task->add(item);
    }
    //终止标记(在需要正常结束的时候添加)
    for (size_t i = 0; i < gc_threadCount; i++)
    {
        downloadItem item(true);
        task->add(item);
    }
}

四、执行下载任务

定义下载任务由5个线程并发执行:

cpp 复制代码
static int gc_threadCount = 5;

下载具体执行将有QRunable类操作:

cpp 复制代码
class taskRunable : public QRunnable
{
public:
	taskRunable(downloadTask* task) :m_task(task) {}

	void run() override 
	{
		while (true)
		{
			//任务中途终止
			if (m_task->isQuit())
				break;
			downloadItem item = m_task->get();
			//任务完成后终止
			if (item.quit)
				break;
			//TODO:下载函数处理item
		}
	}

private:
	downloadTask* m_task;
};

下载任务将有线程池执行。

cpp 复制代码
void startDownload(downloadTask* task)
{
    QThreadPool::globalInstance()->setMaxThreadCount(gc_threadCount);
    for (int i = 0; i < gc_threadCount; ++i)
    {
        auto runable = new taskRunable(task);
        QThreadPool::globalInstance()->start(runable);
    }
    QThreadPool::globalInstance()->waitForDone();
    return;
}

如需要中途结束下载任务,则执行task->quit();

五、总结

至此,我们通过改造Qt的队列,实现了阻塞式的元素获取。并通过线城池的方式,实现了并发下载的示例。

相关推荐
愚润求学9 分钟前
【Linux】动静态库链接原理
linux·运维·服务器·开发语言·笔记
呦呦彬17 分钟前
【问题排查】easyexcel日志打印Empty row!
java·开发语言·log4j
Tummer836327 分钟前
C#+WPF+prism+materialdesign创建工具主界面框架
开发语言·c#·wpf
九章云极AladdinEdu34 分钟前
GPU与NPU异构计算任务划分算法研究:基于强化学习的Transformer负载均衡实践
java·开发语言·人工智能·深度学习·测试工具·负载均衡·transformer
好吃的肘子1 小时前
MongoDB 应用实战
大数据·开发语言·数据库·算法·mongodb·全文检索
ghost1431 小时前
C#学习第23天:面向对象设计模式
开发语言·学习·设计模式·c#
小白学大数据1 小时前
Scrapy框架下地图爬虫的进度监控与优化策略
开发语言·爬虫·python·scrapy·数据分析
立秋67891 小时前
用Python绘制梦幻星空
开发语言·python·pygame
汉克老师1 小时前
GESP2025年3月认证C++二级( 第三部分编程题(1)等差矩阵)
c++·算法·矩阵·gesp二级·gesp2级
明月看潮生1 小时前
青少年编程与数学 02-019 Rust 编程基础 16课题、包、单元包及模块
开发语言·青少年编程·rust·编程与数学