【问题标题】:Print permutation of {0, 1, 2, 3} by multithread program (C++)通过多线程程序 (C++) 打印 {0, 1, 2, 3} 的排列
【发布时间】:2016-08-20 21:29:34
【问题描述】:

我想打印一个用 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 的存在。

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

【问题讨论】:

  • [OT]:使用睡眠方法代替for (int i = 0; i &lt; 10000; i++);
  • 顺便说一句,获得随机排列的更好方法是使用std::shuffle
  • 感谢 Jarod42 的评论,但对我来说真正的目标不是创建排列,而是了解这个多线程进程的行为。

标签: c++ multithreading c++11 permutation


【解决方案1】:

您正在通过引用捕获i

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

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

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

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

【讨论】:

  • 你是对的。我想我应该使用 [=] 捕获而不是 [&]。
【解决方案2】:

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

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

[edit: 从 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 从队列中取出的线程随后被抢占并且不会再次执行,直到 123 已将他们的结果推送到结果队列中。

【讨论】:

  • @M.M 编辑了答案。
【解决方案3】:

正如 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;
}

阿姆兰·阿卜杜勒卡德。

【讨论】:

  • 线程太没用了......因为它们是连续完成的。向量包含已完成的线程...
  • @Jarod42。如果您有任何其他建议,我将不胜感激。
  • 我的意思是你的代码基本上相当于int main() { for (int i = 0; i &lt; 4; i++) func(i); system("pause");}
猜你喜欢
  • 2020-01-10
  • 2014-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多