通过多线程程序 (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);
}
我们仍然不能保证按顺序返回结果:很可能从队列中取出 0
的线程然后被抢占并且直到 [=14] 才再次执行=]、2
和 3
已将其结果推送到结果队列中。
我想打印由用 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);
}
我们仍然不能保证按顺序返回结果:很可能从队列中取出 0
的线程然后被抢占并且直到 [=14] 才再次执行=]、2
和 3
已将其结果推送到结果队列中。