<小时/> 的序: 当我输入新代码时,我将我的函数声明为 pass-by-reference-to-const 而不考虑(出于习惯),有时我必须回过头来改变它这不是我的意思。
我正在编写一个无限期运行的worker-thread类,并提供字符串(来自另一个线程)进行处理。当我意识到我已将该函数声明为 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()
都会创建一个字符串“主线程”,并将其移动到线程函数void workthread(const std::string &)
。然后,线程函数更改左值的数据并返回。主要继续到一个循环,它只是将其本地字符串打印到控制台(有一些额外的眼睛糖果,以便很容易看到屏幕上发生的事情)。这是输出:
所以,它没有像我预期的那样起作用。我原以为线程实例化会将str
“移动”到线程函数(在进程中清空它的数据),而线程对函数的字符串参数的赋值将没有任何影响。但很明显,正如输出所示。
这必须与我使用lambda构造_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)
它按预期工作:
Q1:为什么两个实例lambda vs bind (我猜?)会产生不同的结果?
Q2:我实际上通过将此声明为传递参考来购买任何东西吗?
我应该注意到实际程序非常关键,并且打算在专用服务器上不间断运行多年。我正在努力使软件尽可能低开销,以确保它可以保持同步(使用外部时钟),而不是累积时间错误。
答案 0 :(得分:3)
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
's constructor(该列表中的#3; 3),您会看到它&#34;副本&#34;函数调用的参数;它大致称呼:
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
移动后 - 字符串保留在&#34;有效但未指定的状态&#34; (std::string
&#39; s move constructor和move assignment operator)。在将str
移出后,您不能指望LaravelLocalization::getCurrentLocale()
为空字符串。