C ++中的Threadpool / Queuing系统

时间:2013-08-31 21:16:21

标签: c++ multithreading qt c++11

我有一种情况需要做一些繁重的计算。我发现细分我的数据然后将它们合并在一起是最快的(随着尺寸增加,时间增加得更快,所以分裂是合乎逻辑的)。

它应该能够为应用程序提供数据大小,比如说一百万个双倍值。

我现在所拥有的是将基于此大小的创建数据发送到某个函数,在计算后返回它,然后循环返回以将此数据卸载到主向量中。

我想发送200个部分,一个“最后”部分。例如,给出size = 1000005最初将执行此函数5000次,然后最后一次执行大小为5的数据。

int size = 1000000;
int times = size / 200; // 5000
int leftover = size % 200; // 0, this not performed

QVector<double> x(size);
QVector<double> y(size);

x = createData(size);
x = createData(size);

for (int i = 0; i < times; i++)
{
    holder = createData(200);
    QVector<double> tempx = x.mid(i*200, 200);
    QVector<double> tempy = y.mid(i*200, 200);
    holder = myfunction(tempx, tempy, 200);  // let it now just return `tempy`
    for (int j = 0; j < 200; j++)
    {
        y[i*200 + j] = holder[j];
    }
}
// leftover function here, really similar to this part before.

// plotting function here

最后,x将保持初始化状态,y将进行计算。

由于这些代码部分可以彼此分开并且速度至关重要,所以我想使用几个核心。

以下进一步描述了这种情况:

  • 这些函数调用是相互独立的,只有在向量完成时最终才能绘制结果。
  • 每次通话的完成时间会有很大变化。
  • times的数量应该是可变的。

我读到了一些关于最大线程被建议为核心数量(至少作为起点)的东西,因为使用太多线程会降低进程的速度。考虑到这种情况,排队系统/线程池似乎有意义,因为不会浪费时间,而一个线程有一些简单的工作,而其他线程则通过更难的工作减慢一切。

虽然在几十个教程中使用一些(通常是2个)线程打印一些消息似乎很容易,但是任何人都可以提供有关如何返回向量并将这些线程安全地卸载到主函数中的更详细的帮助,以及如何创建一个线程池所以时间不会浪费?

使用Ubuntu 13.04,Qt和C ++ 11x,虽然没关系。

3 个答案:

答案 0 :(得分:4)

首先,写一个踏板池很难。如果你真的想学习如何编写一本,Antony Williams编写的“C ++ Concurrency in Action”一书将教你如何实现这一目标。

然而,你的情况似乎是一个简单的parallel_for完全适合的情况。所以我建议使用Intel Threading Building Blocks library。该库的优点是它具有非常好的线程池,并且与C ++ 11特性相当好。

示例代码:

#include "tbb/task_scheduler_init.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#include "tbb/tbb_thread.h"
#include <vector>

int main() {
  tbb::task_scheduler_init init(tbb::tbb_thread::hardware_concurrency());
  std::vector<double> a(1000);
  std::vector<double> c(1000);
  std::vector<double> b(1000);

  std::fill(b.begin(), b.end(), 1);
  std::fill(c.begin(), c.end(), 1);

  auto f = [&](const tbb::blocked_range<size_t>& r) {
    for(size_t j=r.begin(); j!=r.end(); ++j) a[j] = b[j] + c[j];    
  };
  size_t hint_number_iterations_per_thread = 100;
  tbb::parallel_for(tbb::blocked_range<size_t>(0, 1000, hint_number_iterations_per_thread), f);
  return 0;
}

完成!英特尔TBB有一个非常好的线程池,它将尝试调整每个线程的工作负载。只要hint_number_iterations_per_thread不是一个疯狂的数字,它就会非常接近最优解决方案

顺便说一句:intel TBB是一个与大多数编译器一起工作的开源库!

答案 1 :(得分:1)

您无需创建任何内容。如果您使用Qt,则问题已经解决。您可以从QRunnable派生一个类,然后将其传递给QThreadPool以执行。

你可以指示QThreadPool应该同时运行多少个线程(任何额外的东西只是在队列中等待,直到一个插槽打开)但这不应该是必要的,因为QThreadPool根据你的设置限制通常足够好的建筑。

QThreadPool

QRunnable

答案 2 :(得分:0)

比创建QThreadPool和扩展QRunabble更简单,您可以使用QtConcurrent库。具体使用QtConcurrent::mapped函数,它接受一个开始迭代器和一个结束迭代器,以及一个函数(可以是一个lambda),并在内部处理线程池的创建和执行。

有两种变体:“mapped”将QFuture返回给结果但不阻止当前线程,而“blockingMapped”直接返回结果列表。

要对大的整数向量求平方,您可以执行以下操作:

std::vector<int> myInts = ....

QVector<int> result = QtConcurrent::blockingMapped(myInts.begin(), myInts.end(), [](int x) { return x*x}; });