通过多线程程序 (C++) 打印 {0, 1, 2, 3} 的排列

Print permutation of {0, 1, 2, 3} by multithread program (C++)

我想打印由用 C++11 编写的多线程程序设置的 {0, 1, 2, 3} 的排列。

源代码是这样的:

#include <iostream>
#include <stdio.h>
#include <thread>
#include <vector>
#include <chrono>

using namespace std;


void func(int index);

int main()
{
    vector<thread> threads;

    for (int i = 0; i < 4; i++)
    {
        auto var = [&]()
        {
            return func(i);
        };

        threads.push_back(thread(var));
    }

    for (auto& thread : threads)
        thread.join();
}

void func(int index)
{
    cout << index;

    for (int i = 0; i < 10000; i++);
}

我希望输出 0123 的排列,但我收到奇怪的结果,如下所示:

0223

0133

0124

我不明白这种奇怪的行为,尤其是我无法解释数字 4 的存在。

可能是初学者的错误,谢谢大家的帮助。

您正在通过引用捕获 i

    auto var = [&]()
    {
        return func(i);
    };

因此,当最终启动的线程被启动并运行时,它没有实际的副本,此时它具有 i 的值,但它只有一个引用至 i.

现在可能增加了一两次。如果你认为引用实际上只是一个普通的指针,上面有一层薄薄的化妆品,你应该能够自己弄清楚。线程获得指向 i 的指针,谁知道线程开始运行时它可能已经递增了多少次。

而且,从技术上讲,由于 i 甚至可能超出此处的范围,如果 for 循环在线程开始执行之前终止,则这是未定义的行为。

正如 Sam Varshavchik 提到的 i 超出范围时的未定义行为,我建议加入在 i 存在的循环内创建的每个线程,方法是添加以下内容:

threads[i].join();

并且不要忘记删除 :

for (auto& thread : threads)
    thread.join();

你的主要功能应该是这样的:

int main()
{
    vector<thread> threads;

    for (int i = 0; i < 4; i++)
    {

        auto var = [&]()
        {
            return func(i);
        };

        threads.push_back(thread(var));

        threads[i].join(); // joining the thread after its creation.
    }

    system("pause");
    return 0;
}

阿姆兰·阿卜杜勒卡德尔。

您正在以三种方式调用未定义的行为:

首先,您是通过引用而不是值来捕获堆栈变量的值,因此当线程启动时它将调用 lambda 并使用 i 当时的当前值而不是捕获时的值。

[编辑:从 C++11 开始不再正确] 其次是 cout 的线程安全性

第三个是线程执行顺序的假设,但无法保证。 [编辑:]这不仅包括它们开始的顺序,还包括它们访问 cout 以写入输出的顺序。

但是你需要解决执行顺序吗?

如果这样做,则不要将值传递给线程,而是将它们放入队列并授予线程访问队列的权限。

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <queue>

class LockedQueue {
    std::queue<int> queue_;
    mutable std::mutex mutex_;
public:
    LockedQueue() = default;

    // these don't have to be deleted, but you'd have to decide whether or
    // not each operation needed to invoke a lock, and in the case of operator=
    // you have two mutexes to contend with.
    LockedQueue(const LockedQueue&) = delete;
    LockedQueue(LockedQueue&&) = delete;
    LockedQueue& operator=(const LockedQueue&) = delete;
    LockedQueue& operator=(LockedQueue&&) = delete;

    void push(int value) {
        std::lock_guard<std::mutex> lock(mutex_);
        queue_.push(value);
    }
    int pop() {
        std::lock_guard<std::mutex> lock(mutex_);
        int value = queue_.front();
        queue_.pop();
        return value;
    }
    bool empty() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.empty();
    }
};

void func(LockedQueue& work, LockedQueue& results);

int main()
{
    LockedQueue work, results;
    std::vector<std::thread> threads;

    for (int i = 0; i < 4; i++)
    {
        work.push(i);
        threads.emplace_back(func, std::ref(work), std::ref(results));
    }

    for (auto& thread : threads)
        thread.join();

    while (!results.empty()) {
        int i = results.pop();
        std::cout << i;
    }
}

void func(LockedQueue& work, LockedQueue& results)
{
    int index = work.pop();
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);
    results.push(index);
}

http://ideone.com/7G0JEO

我们仍然不能保证按顺序返回结果:很可能从队列中取出 0 的线程然后被抢占并且直到 [=14] 才再次执行=]、23 已将其结果推送到结果队列中。