[英]Pass parameters to std::thread wrapper
我想實現一個小線程包裝器,如果一個線程仍處於活動狀態,或者該線程已完成其工作,它將提供信息。 為此,我需要將函數及其參數傳遞給線程類到另一個函數。 我有一個簡單的實現應該可以工作,但不能讓它編譯,我無法弄清楚該怎么做才能使它工作。
這是我的代碼:
#include <unistd.h>
#include <iomanip>
#include <iostream>
#include <thread>
#include <utility>
class ManagedThread
{
public:
template< class Function, class... Args> explicit ManagedThread( Function&& f, Args&&... args);
bool isActive() const { return mActive; }
private:
volatile bool mActive;
std::thread mThread;
};
template< class Function, class... Args>
void threadFunction( volatile bool& active_flag, Function&& f, Args&&... args)
{
active_flag = true;
f( args...);
active_flag = false;
}
template< class Function, class... Args>
ManagedThread::ManagedThread( Function&& f, Args&&... args):
mActive( false),
mThread( threadFunction< Function, Args...>, std::ref( mActive), f, args...)
{
}
static void func() { std::cout << "thread 1" << std::endl; }
int main() {
ManagedThread mt1( func);
std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() << std::endl;
::sleep( 1);
std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() << std::endl;
return 0;
}
我得到的編譯器錯誤:
In file included from /usr/include/c++/5/thread:39:0,
from prog.cpp:4:
/usr/include/c++/5/functional: In instantiation of 'struct std::_Bind_simple<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>':
/usr/include/c++/5/thread:137:59: required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(volatile bool&, void (&)()); _Args = {std::reference_wrapper<volatile bool>, void (&)()}]'
prog.cpp:28:82: required from 'ManagedThread::ManagedThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]'
prog.cpp:35:28: required from here
/usr/include/c++/5/functional:1505:61: error: no type named 'type' in 'class std::result_of<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>'
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/5/functional:1526:9: error: no type named 'type' in 'class std::result_of<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>'
_M_invoke(_Index_tuple<_Indices...>)
^
現場示例如下: https : //ideone.com/jhBF1q
在錯誤消息中,您可以看到差異void (*)()
vs void (&)()
。 那是因為std :: thread的構造函數參數是std::decay
ed 。
添加std::ref
到f
:
template< class Function, class... Args>
ManagedThread::ManagedThread( Function&& f, Args&&... args):
mActive( false),
mThread( threadFunction< Function, Args...>, std::ref(mActive), std::ref(f), std::forward<Args>(args)...)
{
}
@ O'Neil的答案是正確的,但我想提供一個簡單的lambda方法,因為你已將其標記為C++14
。
template<class Function, class... Args>
ManagedThread::ManagedThread(Function&& f, Args&&... args):
mActive(false),
mThread([&] /*()*/ { // uncomment if C++11 compatibility needed
mActive = true;
std::forward<Function>(f)(std::forward<Args>(args)...);
mActive = false;
})
{}
這將不再需要外部功能。
O'Neil和DeiDei先來到這里,就我所知,他們是正確的。 但是,我仍然在發布我的問題解決方案。
這里有更好的效果:
#include <atomic>
#include <thread>
#include <utility>
class ManagedThread {
public: /* Methods: */
template <class F, class ... Args>
explicit ManagedThread(F && f, Args && ... args)
: m_thread(
[func=std::forward<F>(f), flag=&m_active](Args && ... args)
noexcept(noexcept(f(std::forward<Args>(args)...)))
{
func(std::forward<Args>(args)...);
flag->store(false, std::memory_order_release);
},
std::forward<Args>(args)...)
{}
bool isActive() const noexcept
{ return m_active.load(std::memory_order_acquire); }
private: /* Fields: */
std::atomic<bool> m_active{true};
std::thread m_thread;
};
它使用lambdas代替,並且正確地使用std::atomic<bool>
而不是volatile
來同步狀態,並且還包括相應的noexcept()
說明符。
另請注意,底層std::thread
在銷毀之前未正確連接或分離,因此導致調用std::terminate()
。
我重寫了測試代碼:
#include <chrono>
#include <iostream>
int main() {
ManagedThread mt1(
[]() noexcept
{ std::this_thread::sleep_for(std::chrono::milliseconds(500)); });
std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive()
<< std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive()
<< std::endl;
}
以下是簡單,優雅,據我所知,所有當前答案中最正確的(見下文):
template < class Function, class... Args >
ManagedThread::ManagedThread( Function&& fn, Args&&... args ) :
mActive(false),
mThread(
[this]( auto&& fn2, auto&&... args2 ) -> void {
mActive = true;
fn2(std::forward<Args>(args2)...);
mActive = false;
},
std::forward<Function>(fn), std::forward<Args>(args)...
)
{}
DeiDei 的答案有一個微妙但重要的缺陷:lambda通過引用獲取堆棧變量,因此如果線程啟動並嘗試使用,構造函數返回后的堆棧變量,則可以獲得堆棧使用后免費錯誤。 實際上,使用-fsanitize=address
編譯通常足以證明問題。
奧尼爾的答案在例如任何參數都是左值時都不起作用,並且有點笨拙。
jotik 的答案很接近,但是當參數是左值時,它們也不起作用,也不是成員函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.