我在c ++框架中工作,主要是在c ++ 11之前编写,它允许我们从一个线程向另一个线程发送事件(假设接收线程正在运行一个事件队列,所以它&# 39; s主要用于从辅助线程向主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,它建立在当前框架功能之上(省略错误检查,清理等):
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;
};
这让我可以一次性开火#34;然而,事件到主线程,但无法发送参数,它的功能非常有限。
我想让这个类使用可变参数模板,以便我可以将任何内容传递给callInMainThread
函数。但是,我还没有能够弄清楚如何在事件中存储参数包并将其传递给call()
内的函数。所以我的问题是:
答案 0 :(得分:3)
不是将接口结构与调用分开,而是使用lambdas的强大功能将参数捕获到函数对象本身(闭包)中,以便每次调用都相同。没有基本线程通信对象封装函数类型。相反,在调用时传递整个函数。转到这个界面:
threadBoundary.callInMainThread([value](){doSomething(value);});
如果您传递的所有内容都是不带参数的函数对象,则它很容易通过并且易于调用。捕捉你需要通过的任何东西。
对于某些接口和实现思路,请查看boost::asio
如何实现其io_service
,它允许您将函数调用发布到其他线程,就像这样。
答案 1 :(得分:0)
一个nullary函数调用更容易在线程边界发送。并且很容易捕获您想要传递给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... );
};
}
};
freeze_function<void(int)>
可以使用int
的lambda构造。然后,您可以将int
传递给它,然后返回std::function<void()>
并绑定int
。
但我认为这不是必需的。
(从freeze_function
获得最佳效率所需的额外工作;它不必要地复制args...
。我将args...
扔进一个元组然后在lambda中解压缩它们,也许使用相当于C ++ 17中的std::apply
。)
可以通过添加签名(使其成为模板)将其烘焙到ThreadBoundary
中,或者可以用于在某处存储未实现的函数调用并在传递结果{{1}之前立即冻结它们} std::function<void()>
。