一、 QtConcurrent::run简介
QtConcurrent
是Qt框架中的一个模块,旨在提供方便且高效的并行编程实现方式。其中QtConcurrent::run
是使用最广泛的方法。调用QtConcurrent::run
时,将会自动将函数以一个新的线程运行,并将函数返回结果传递回主线程。
QtConcurrent::run
的用法简单,仅需传递一个函数指针和函数参数,即可使用多线程的方式运行函数。使用QtConcurrent::run
可以充分利用多核处理器,提高程序效率,同时避免了使用底层多线程时的一些容易出现的线程安全问题。
二、 使用QtConcurrent::run进行多线程编程
我们来看一个简单的例子,假设我们需要对一个字符串进行加密处理:
QString encrypt(QString str, int key) {
for(int i = 0; i < str.length(); ++i) {
str[i] = str[i] ^ key;
}
return str;
}
接下来,我们通过QtConcurrent::run
在新线程中运行这个函数:
QFuture
future = QtConcurrent::run(encrypt, "hello world", 42);
使用QtConcurrent::run
时,它不会占用主线程的时间,而是会自动启用一个新的线程去执行函数,这个新的线程会在后台运行,直到函数执行完毕并返回结果。
在这里我们用QFuture保存异步操作的结果,QFuture表示未来的结果,这个结果可以在需要的时候被获取。
下面,我们通过QFuture
对象获取异步操作的结果:
QString result = future.result();
在需要异步操作结果的地方使用future.result()
来获取异步操作结果,如果异步操作没有完成,future.result()
就会一直等待,直到异步操作完成为止。
三、使用QtConcurrent::run实现线程安全的容器操作
除了简单的函数运算外,QtConcurrent::run
还可以用于执行容器的并行操作,比如在多线程中对QList
的修改操作。
在多线程中,对容器的修改操作需要考虑到线程安全,以避免出现竞态条件等线程安全问题。一般的解决方法是添加互斥锁,但互斥锁会导致线程的等待与阻塞,降低程序的效率。
使用QtConcurrent::run
可以方便地实现线程安全的容器操作,使用QtConcurrent::run
时需要将需要并行执行的操作函数写成可重入的函数。
以下是一个使用QtConcurrent::run
实现并行容器操作的例子,这里我们实现一个在多线程中并行地对一个QList
进行排序的操作。
void sort(QList
& list) {
std::sort(list.begin(), list.end());
}
void parallelSort(QList
& list) {
QVector
> futures;
const int threadCount = QThread::idealThreadCount();
const int chunkSize = list.size() / threadCount;
int begin = 0, end = chunkSize;
for(int i = 0; i < threadCount; ++i) {
if(i == threadCount - 1) {
end = list.size();
}
futures.append(QtConcurrent::run(sort, list.mid(begin, end - begin)));
begin = end;
end += chunkSize;
}
for(int i = 0; i < futures.size(); ++i) {
futures[i].waitForFinished();
}
std::inplace_merge(list.begin(), list.begin() + chunkSize, list.end());
}
在parallelSort
函数中,我们将要排序的QList
按照QThread::idealThreadCount()
的数量进行分割,并将每个子序列交给一个新线程去执行排序操作。
执行完所有子线程后,我们使用std::inplace_merge
函数合并所有的排好序的子序列。
四、QtConcurrent::run的使用注意事项
虽然QtConcurrent::run
提供了一种方便且高效的多线程编程实现方式,但其也有其适用的范围和使用注意事项:
- 线程池内的线程更加倾向于长期存在,不要把耗时很短的任务放进线程池中,可能会导致线程池耗费过多的系统资源,不利于程序性能。
// 不建议写法 for (int i = 0; i < 100; ++i) { QtConcurrent::run([](){ /* do something */ }); }
QtConcurrent::run
会在新线程中自动执行函数,但也可以使用QThread
手动地创建新线程。QThread* thread1 = new QThread(); MyObject* object1 = new MyObject(); object1->moveToThread(thread1); QObject::connect(thread1, &QThread::started, object1, &MyObject::mySlot); thread1->start();
- 使用
QtConcurrent::run
时需要注意线程安全,避免并发操作数据时出现竞争问题。 QtConcurrent::run
可以将函数的返回结果传递到主线程,但是如果异步操作返回的结果需要马上使用,应使用QFuture::result()
来等待异步操作的结束并获取结果,否则可能会在异步操作没结束前使用该结果。QString result = QtConcurrent::run(someFunction, params).result();
五、结语
QtConcurrent::run
是Qt框架中提供的一种方便、高效的多线程编程实现方式。使用QtConcurrent::run
可以避免使用底层多线程时的一些容易出现的线程安全问题,并充分利用多核处理器提高程序效率。
在使用QtConcurrent::run
时需要注意线程安全,合理地使用QThread
,避免将耗时很短的任务放进线程池中,正确地使用异步的结果等技巧,可以充分发挥QtConcurrent::run
的优势。