[英]Call std::function in different thread
我正在一個c ++框架中工作,主要是在c ++ 11之前編寫的,它使我們能夠將事件從線程觸發到另一個線程(假設接收線程正在運行事件隊列,因此它主要用於觸發事件以主UI線程(來自幫助程序線程)。
當前,實現此目的的代碼非常冗長-需要我們定義兩個類:
我們最近已移至c ++ 11/14,並一直在努力更新許多代碼以使用智能指針,標准容器和lambda。 我想編寫一個通用類,使我可以發送lambda以便在其他線程中運行。 就像是:
mBoundary = make_unique<ThreadBoundary>( []( int value ) { doSomething( value ); } );
mBoundary->callInMainThread( 47 );
ThreadBoundary boundary2( []( std::string value ) { displayString( value ); } );
boundary2.callInMainThreadWait( "Show this string to the user" );
作為最初的嘗試,我目前正在使用不帶任何參數的lambda進行工作,該lambda基於當前框架功能(省略錯誤檢查,清除等)構建:
class ThreadBoundary
{
public:
ThreadBoundary( std::function<void()> function ):mFunction( function )
{
mListener = make_shared<ThreadBoundaryListener>();
cApplication::addThreadBoundaryListener( mListener );
}
void callInMainThread()
{
cApplication::fireEventInMainThread( new ThreadBoundaryEvent( mFunction ) );
}
class ThreadBoundaryEvent:public FrameworkThreadBoundaryEvent
{
public:
ThreadBoundaryEvent( std::function<void()> function )
{
mFunction = function;
}
void call() { mFunction(); }
private:
std::function<void()> mFunction;
};
class ThreadBoundaryListener:public FrameworkThreadBoundaryListener
{
public:
ThreadBoundaryListener() {}
void handleEvent( const FrameworkThreadBoundaryEvent* event )
{
dynamic_cast<const ThreadBoundaryEvent*>( event )->call();
}
};
private:
shared_ptr<ThreadBoundaryListener> mListener;
std::function<void()> mFunction;
};
這使我可以將“一次性”事件觸發到主線程,但是,由於無法發送參數,因此它的功能非常有限。
我想讓此類使用可變參數模板,以便可以將任何內容傳遞給callInMainThread
函數。 但是,我還無法弄清楚如何在事件中存儲參數包並將其傳遞給call()
內部的函數。 所以我的問題是:
不要將接口構造與調用分開,而應使用lambda的功能將參數捕獲到函數對象本身(閉包)中,以便每次調用都相同。 沒有基本的線程通信對象封裝函數類型。 而是在調用時傳遞整個函數。 轉到此界面:
threadBoundary.callInMainThread([value](){doSomething(value);});
如果您傳遞的所有內容都是不帶參數的函數對象,那么傳遞和調用都很容易。 捕獲您需要通過的所有內容。
有關一些接口和實現的想法,請查看boost::asio
如何實現其io_service
,它使您可以像這樣將函數調用發布到其他線程。
空函數調用更容易跨線程邊界發送。 而且很容易捕獲要傳遞到lambda捕獲列表中的參數。
我會接受的。
但是,您可以編寫代碼將非空函數調用轉換為空函數。 如果要“凍結”不帶參數的函數調用,則簽名也必須凍結。
tempate<class Sig>
struct freeze_function;
tempate<class R, class...Args>
struct freeze_function<R(Args...)> {
std::function< R(Args...) > to_freeze;
freeze_function() = default;
freeze_function( freeze_function&& ) = default;
freeze_function( freeze_function const& ) = default;
freeze_function& operator=( freeze_function&& ) = default;
freeze_function& operator=( freeze_function const& ) = default;
freeze_function( std::function< void(Args...) > f ):to_freeze(std::move(f)) {}
std::function<R()> operator()( Args... args ) const {
return [=, frozen=this->to_freeze]->R{
return frozen( args... );
};
}
};
可以通過采用int
的lambda構造freeze_function<void(int)>
。 然后,您可以將其傳遞給一個int
並且它返回帶有該int
綁定的std::function<void()>
。
但是我認為這不是必需的。
(為了從freeze_function
獲得最佳效率需要進行額外的工作;它不必要地復制args...
我freeze_function
args...
扔到一個元組中,然后在lambda中解壓縮它們,也許使用等效於std::apply
from C ++ 17)。
可以通過添加簽名(使其成為模板)將其烘焙到ThreadBoundary
,也可以將未實現的函數調用存儲在某個位置,並在將生成的std::function<void()>
傳遞給ThreadBoundary
之前立即凍結它們。 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.