用C ++创建100个线程

问题描述 投票:-1回答:3

我试图了解C ++中的多线程。我试图通过使用一个线程数组创建100个带循环的线程,但我收到一个错误。它给出了这个错误:

error: array initializer must be an initializer list
thread threads[i](task1, list[i]);

这是代码:

static int list [100] = {};
thread threads [100] = {};


void task1(int n)
{
  for (int i = 0; i < 10; i++)
    n = n + 1;
}

int main()
{
  for (int i = 0; i < sizeof(list); i++){
    thread threads[i](task1, list[i]);
    threads[i].join();
  }

  int total = 0;
  for (int i = 0; i < sizeof(list); i++)
    total += list[i];

  cout << total << endl;

  return 0;
}
c++ multithreading
3个回答
4
投票

您的数组thread threads [100] = {};创建了100个默认初始化非活动线程。

您可以通过更改循环来替换这些默认线程,如下所示:

for (int i = 0; i < sizeof(list); i++){
    threads[i] = thread(task1, list[i]);    // <---- valid syntax
    threads[i].join();
}

这是说:

  • 很可惜启动线程并立即加入它。如果你期望一些真正的多线程,最好有第二个加入循环。
  • 使用vector<thread>是一种更好的做法,这样只有在需要时才能构造线程(参见其他答案)
  • 您可能对std::thread::hardware_concurrency()感兴趣,以找出有关硬件支持的真正并发线程数的提示,以免在太多上下文切换中创建太多线程和松散性能。

4
投票

你应该这样做。必须使用将运行的任务和参数初始化线程。当你创建一个包含100个线程的数组时,它会将它们全部初始化为什么,这是一种无用的浪费。另外,你使用sizeof是错误的。 sizeof将以字节为单位给出数据结构的原始大小,并且不会为您提供数组元素的数量。如果你想使用sizeof来获得数组元素的数量,你应该做一些类似sizeof(array) / sizeof(<element type>)的事情,在你的情况下是sizeof(list) / sizeof(int)。但是,实际上,你可能不应该在C ++中使用C风格的数组,而且在这种情况下肯定不会。

你应该在运行时建立一个vector并使用emplace_back逐个创建线程。此外,您正在以非常C-ish的方式编写代码。你应该写C ++,而不是C.(另外,一个宠儿,总是更喜欢前缀++。这里没什么关系,但是,有时因为性能原因很重要,如果你养成了习惯性的话使用前缀版本,你不会有问题。)这可能是这样的:

#include <vector>
#include <array>
#include <thread>
#include <iostream>

using ::std::thread;
using ::std::array;
using ::std::vector;
using ::std::cout;
using ::std::endl;
using ::std::ref;

static array<int, 100> list {};
vector<thread> threads;

void task1(int &n)
{
  for (int i = 0; i < 10; ++i)
    n = n + 1;
}

int main()
{
  threads.reserve(list.size());  // Not needed, an optimization.
  for (int &n : list) {  // Use a range-based for loop, not an explicit counting loop
      threads.emplace_back(task1, ::std::ref(n));
  }
  for (auto &thr : threads) {
      thr.join();
  }

  int total = 0;
  for (int const &n : list) {
    total += n;
  }    
  cout << total << endl;

  return 0;
}

现在,因为这是一个玩具程序,我不会批评你决定随机创建100个线程。实际上,这是一个坏主意。您希望将创建的线程数量定制为您拥有的CPU数量,或者操作系统将浪费大量时间在忙线程之间切换。以这种方式限制线程将涉及使用::std::thread::hardware_concurrency等函数来查询可用的内核数量,并使用该信息来决定运行时有多少线程。

当然,这并不总是最简单的编写程序的方法,为简单起见,您可以选择任意数量的线程并坚持使用它。但是,如果你这样做,它应该是你可以逃脱的一个小数字。

但是,你创建线程的骑士方法,以及你在创建它之后使用每个线程进行joined的方式告诉我你并没有真正理解线程的确切做法。如果您立即使用线程join,则线程不会同时运行。你正在启动它,然后在开始下一个之前立即等待它完成。

此外,您正在使用线程执行的微小任务是对它们的使用不当。线程创建起来有些昂贵。每次调用时都会创建一个线程的函数调用有数十,数百甚至数千微秒的开销。这听起来不是很多时间,但你必须记住,典型的函数调用开销就像是1/50微秒或甚至1/100微秒。因此,通过创建线程调用函数的开销是正常调用函数的数万倍。

这意味着您应该在一个线程中执行相当大的任务。如果任务至少花费一毫秒的时间,则不应创建线程。理想情况下,您应该创建一个线程,然后使用线程安全队列来发送它。这将减少每件事的开销。由于其开销要小得多,因此您可以经济地在线程中执行较小的任务。

当您只是尝试使用一个小程序来创建线程时,所有这些都需要考虑。但是,编写糟糕的多线程程序对世界造成了可怕的影响,尤其是对自己而言。除了学习线程接口的基础知识之外,在使用它们之前,您应该彻底了解它们。它们是一种很容易被误用的工具。


1
投票

在解决问题时运行尽可能多的线程可能会导致程序上下文切换很多,因此无法尽快解决。您通常不希望运行的线程多于硬件支持的线程数(通常少一个)。

另一件经常让世界变得与众不同的是false sharing,它可以大大降低你的表现。

如果您使用的是支持新C ++ 17执行策略的编译器(如VS2017或g ++ 9),您可以使用并行执行策略for_each执行std::execution::par循环来完成您的工作。

下面的例子(我已经增加了很多工作量)在我的计算机上花了3.2秒,当我合理地确保避免错误共享(使用alignas(std::hardware_destructive_interference_size))和21.3秒使用默认对齐。

#include <iostream>
#include <array> // std::array
#include <execution> // std::execution::par
#include <new> // std::hardware_destructive_interference_size

struct bork {
    alignas(std::hardware_destructive_interference_size) int n;
    // int n; // default alignment
};

std::array<bork, 1000> list{ 0 };

int main() {
    std::for_each(std::execution::par, list.begin(), list.end(), [](auto& b) {
            for (int i = 0; i < 100000000; i++) b.n = b.n + 1;
        }
    );

    long long total = 0;
    for (const auto& b : list) total += b.n;

    std::cout << total << "\n";
}
© www.soinside.com 2019 - 2024. All rights reserved.