繁体   English   中英

线程安全和std :: move

[英]Thread safety and std::move


前言:当我输入新代码时,我将我的函数声明为const的按引用传递 (出于习惯),有时当我意识到这不是我的意思时,必须回去更改它去做。

我正在编写一个可无限期运行的工作线程类,并从另一个线程获取字符串进行处理。 当我意识到我已经将该函数声明为pass-by-ref时 ,为了线程安全,我又将其更改为pass-by-value

但是,由于我想尽可能地提高速度和效率,因此我停下了脚步,首先探索各种选择。 我写了一些测试例程-发现我对某些关键概念不了解。


关键点:我首先在下面编写了没有注释行的测试代码:

 // std::thread _thread(workthread, move(str)); // Thread safe (contents are moved) 

因此,暂时忽略该行。

 #include <iostream> #include <string> #include <thread> #include <chrono> #include <atomic> std::atomic<bool> done = false; void workthread(const std::string &str) { std::string &s = const_cast<std::string &>(str); s = "Work Thread"; // test to see if this changes the main thread's string } // This just watches for <enter> on the keyboard in order to quit the program. void quitmonitor() { std::getchar(); done = true; } int main(int argc, char **argv) { std::thread _monitor(quitmonitor); std::string str("Main Thread"); std::thread _thread([&]{workthread(std::move(str));}); // Not thread safe (address is copied) // std::thread _thread(workthread, move(str)); // Thread safe (contents are moved) const auto minlen(str.length()); const auto maxlen(minlen ? minlen*2 : 15); bool going_up = true; while (!done) { if (going_up) str.push_back('+'); else str.pop_back(); if (str.length() == minlen) going_up = true; if (str.length() == maxlen) going_up = false; std::cout << str << "\\n"; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } _thread.join(); _monitor.join(); } 

main()所做的只是创建一个字符串“ Main Thread” ,并将其移动到线程函数void workthread(const std::string &) 然后,线程函数更改左值的数据并返回。 主程序继续执行一个循环,该循环仅将其本地字符串打印到控制台(带有一些额外的功能,可以很容易地在屏幕上看到发生的事情)。 这是输出:

用lambda调用的函数

因此,它没有按我预期的那样工作。 我以为线程实例化会将str “移动”到线程函数(清空进程中的数据),并且线程对该函数的string参数的赋值不会有任何影响。 但显然,确实如此,如输出所示。

这与我用lambda构造_thread有关: std::thread _thread([&]{workthread(std::move(str));}); // Not thread safe (address is copied) std::thread _thread([&]{workthread(std::move(str));}); // Not thread safe (address is copied)

因此,然后我将实例化更改为: std::thread _thread(workthread, move(str)); // Thread safe (contents are moved) std::thread _thread(workthread, move(str)); // Thread safe (contents are moved) ,并且按预期工作: 称为绑定的函数

问题1:为什么两个实例lambda vs bind (我猜是?)会产生不同的结果?

问题2:我是否真的通过声明为通过引用来为自己买东西?

我应该注意,实际程序在时间上非常关键,并且打算在专用服务器上连续运行多年。 我正在尝试使该软件的开销尽可能低,以确保该软件可以保持同步(与外部时钟同步),并且不会累积时间错误。

 std::thread _thread([&]{workthread(std::move(str));}); 

创建_thread ,它将调用您的lambda函数,该函数将调用workthread(std::move(str)) 注意, std::move实际上什么也不做; 这只是对右值引用的强制转换。 您永远不会离开str ,只是将引用以环形方式投射到std::string&并分配给它。

这也意味着您在str上存在数据争用,因为您在主线程和_thread之间具有不同步的访问。


这段代码从字符串移走了:

 std::thread _thread(workthread, move(str)); 

如果查看std::thread的构造函数 (该列表上的(3)),您会发现它“复制”了函数调用的参数; 它大致调用:

workthread(decay_copy(std::move(str)))

实际上,该decay_copy确实从字符串移开,因为它按值返回:

 template <class T> std::decay_t<T> decay_copy(T&& v) { return std::forward<T>(v); } 

这就是为什么您将str移开。 但是,您的程序实际上依赖于未指定的行为,因为–从std::string移动之后–字符串处于“有效但未指定的状态”( std::stringmove构造函数move赋值运算符 )。 从它移出后,不能指望str是一个空字符串。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM