隐式类型转换未定义
1 | void f(int i, std::string const& s); |
在这种情况下,正是局部变量 buffer
的指针被传递给新线程,还有一个重要的时机,即函数 oops
会在缓冲在新线程上呗转换为 std::string
之前退出,从而导致未定义的行为。解决之道是在将缓冲传递给 std::thread
的构造函数之前转换为 std::string
。
1 | // 先转换,避免悬浮指针 |
引用对象错误
也有可能的得到相反的情况,对象被复制,而你想要的是引用。这可能发生在当线程正在更新一个通过引用传递来的数据结构时,例如:
1 | void updateWidgetData(Widget w, Widget& data); // 引用 |
尽管 updateWidgetData
希望通过引用传递第二个参数,但 std::thread
的构造函数却并不知道;它无视函数所期望的类型,并且盲目地复制了所提供的值。实际修改的是 data
在线程内部的副本的应用,随着线程的销毁,这些改动都将被舍弃。最后调用 processWidgetData
,将会传递一个未改变的 data
。
对于熟悉 std::bind
的人来说,解决方案也是显而易见的,需要用 std::ref
来包装确实需要被引用的参数。
1 | std::thread t(updateWidgetData, w, std::ref(data); |
新线程调用成员函数
你可以传递一个成员函数的指针作为函数,前提是提供一个合适的对象指针作为第一个参数。
1 | class X |
这段代码将在新线程上调用 x.doWork()
,因为 x 的地址是作为对象指针提供的。你也可以提供参数给这样的成员函数调用:std::thread
构造函数的第三个参数将作为成员函数的第一个参数等等。
转移std::unique_ptr对象所有权
一个有趣的场景是,参数不能被复制但只能被移动:一个对象内保存的数据被转移到另一个对象,使原来的对象变成“空壳”。这种类型的一个例子是 std::unique_ptr
。移动构造函数和移动赋值运算符允许一个对象的所有权在 std::unique_ptr
实例之间进行转移,这种转移给源对象留下一个 NULL 指针。
在源对象是临时的场合,这种移动是自动的;但在源是一个命名值的地方,此转移必须直接通过调用 std::move
来请求。
1 | void processObject(std::unique_ptr<Object>); |
这种所有权可以在实例之间进行转移,因为 std::thread
的实例是可移动的,即使他们不是可复制的。这确保了在允许程序员选择在对象之间转换所有权的时候,在任意时刻只有一个对象与某个特定的执行线程相关联。
笔记摘录自:《C++并发实战》Anthony Williams,章节2.2