我如何锁定我的线程,使我的输出不是这样的:你好...你好...hheelhllelolo.l..o......
std::size_t const nthreads=5;
std::vector<std::thread> my_threads(nthreads);
for(unsigned i=0;i<nthreads;i++)
{
my_threads[i] = std::thread ([] () {std::cout << "hello...";});
}
我如何锁定我的线程,使我的输出不是这样的:你好...你好...hheelhllelolo.l..o......
std::size_t const nthreads=5;
std::vector<std::thread> my_threads(nthreads);
for(unsigned i=0;i<nthreads;i++)
{
my_threads[i] = std::thread ([] () {std::cout << "hello...";});
}
标准说:
多个线程对同步 (27.5.3.4) 标准 iostream 对象的格式化和未格式化输入 (27.7.2.1) 和输出 (27.7.3.1) 函数或标准 C 流的并发访问不应导致数据竞争 (1.10)。[注意:如果用户希望避免交错字符,则仍必须同步多个线程对这些对象和流的并发使用。—结束注释]
— [iostream.objects.overview] 27.4.1 p4
请注意,不产生数据竞争的要求仅适用于标准 iostream 对象(cout、cin、cerr、clog、wcout、wcin、wcerr 和 wclog)并且仅在它们同步时(默认情况下它们是同步的,并且可以使用 sync_with_stdio 成员函数禁用)。
不幸的是,我注意到了两种现象;实现要么提供比要求更严格的保证(例如,所有流对象的线程同步,无论如何,性能都很差)或更少(例如,标准流对象是 sync_with_stdio 产生数据竞争)。MSVC 似乎倾向于前者,而 libc++ 倾向于后者。
无论如何,如注释所示,如果要避免交错字符,则必须自己提供互斥。这是一种方法:
std::mutex m;
struct lockostream {
std::lock_guard<std::mutex> l;
lockostream() : l(m) {}
};
std::ostream &operator<<(std::ostream &os, lockostream const &l) {
return os;
}
std::cout << lockostream() << "Hello, World!\n";
这样,使用 std::cout 创建了一个锁守卫并在表达式的持续时间内存在。您可以将 lockostream 对象模板化以适用于任何 basic_*stream,甚至适用于流的地址,这样您就可以为每个对象设置一个单独的互斥锁。
当然,标准流对象是全局变量,因此您可能希望像避免使用所有全局变量一样避免它们。它们对于学习 C++ 和玩具程序很方便,但您可能希望为真正的程序安排更好的东西。
您必须像使用任何其他资源一样使用正常的锁定技术,否则您会遇到 UB。
std::mutex m;
std::lock_guard<std::mutex> lock(m);
std::cout << "hello hello";
或者你可以使用printf
哪个是线程安全的(在posix上):
printf("hello hello");