[英]Call std::function in different thread
I'm working in a c++ framework, mostly written before c++11, that allows us to fire events from a thread to a different thread (assuming that the receiving thread is running an event queue, so it's mainly used to fire events to the main UI thread from a helper thread). 我正在一个c ++框架中工作,主要是在c ++ 11之前编写的,它使我们能够将事件从线程触发到另一个线程(假设接收线程正在运行事件队列,因此它主要用于触发事件以主UI线程(来自帮助程序线程)。
Currently, the code to accomplish this is very verbose - it requires us to define two classes: 当前,实现此目的的代码非常冗长-需要我们定义两个类:
We've recently moved to c++11/14 and have been working on updating a lot of our code to use smart pointers, standard containers, and lambdas. 我们最近已移至c ++ 11/14,并一直在努力更新许多代码以使用智能指针,标准容器和lambda。 I'd like to write a generic class that allows me to send a lambda to be run in a different thread. 我想编写一个通用类,使我可以发送lambda以便在其他线程中运行。 Something like: 就像是:
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" );
As an initial attempt, I've currently got this working with a lambda that doesn't take any parameters, built on top of the current framework functionality (omitting error checking, cleanup, etc): 作为最初的尝试,我目前正在使用不带任何参数的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;
};
This allows me to fire "one-off" events to the main thread, however, without being able to send along parameters it's functionality is pretty limited. 这使我可以将“一次性”事件触发到主线程,但是,由于无法发送参数,因此它的功能非常有限。
I'd like to make this class use variadic templates so that I can pass anything to the callInMainThread
function. 我想让此类使用可变参数模板,以便可以将任何内容传递给callInMainThread
函数。 However, I haven't been able to figure out how to store the parameter pack in the event and pass it along to the function inside call()
. 但是,我还无法弄清楚如何在事件中存储参数包并将其传递给call()
内部的函数。 So my questions are: 所以我的问题是:
Instead of separating the interface construction from the invocation, use the power of the lambdas to capture arguments into the function object itself (a closure) so that every invocation is the same. 不要将接口构造与调用分开,而应使用lambda的功能将参数捕获到函数对象本身(闭包)中,以便每次调用都相同。 Don't have the basic thread communication object encapsulate a function type. 没有基本的线程通信对象封装函数类型。 Instead, pass the entire function as you call. 而是在调用时传递整个函数。 Go for this interface: 转到此界面:
threadBoundary.callInMainThread([value](){doSomething(value);});
If everything you pass is a function object that takes no parameters, it's easy to pass and easy to call. 如果您传递的所有内容都是不带参数的函数对象,那么传递和调用都很容易。 Capture anything you need to pass. 捕获您需要通过的所有内容。
For some interface and implementation ideas, look at how boost::asio
implements its io_service
, which allows you to post function calls to other threads just like this. 有关一些接口和实现的想法,请查看boost::asio
如何实现其io_service
,它使您可以像这样将函数调用发布到其他线程。
A nullary function call is easier to send accross a thread boundary. 空函数调用更容易跨线程边界发送。 And it is easy to capture the arguments you want to pass into a lambda capture list. 而且很容易捕获要传递到lambda捕获列表中的参数。
I'd embrace that. 我会接受的。
However, you can write code to turn a non-nullary function call into a nullary one pretty easily. 但是,您可以编写代码将非空函数调用转换为空函数。 If you want to "freeze" the function call without the arguments, the signature must also be frozen. 如果要“冻结”不带参数的函数调用,则签名也必须冻结。
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... );
};
}
};
A freeze_function<void(int)>
can be constructed from a lambda taking an int
. 可以通过采用int
的lambda构造freeze_function<void(int)>
。 You can then pass it an int
and it returns a std::function<void()>
with that int
bound. 然后,您可以将其传递给一个int
并且它返回带有该int
绑定的std::function<void()>
。
But I think this isn't required. 但是我认为这不是必需的。
(Extra work required to get optimal efficiency out of freeze_function
; it needlessly copies args...
. I'd toss the args...
into a tuple then unpack them within the lambda, maybe using the equivalent of std::apply
from C++17.) (为了从freeze_function
获得最佳效率需要进行额外的工作;它不必要地复制args...
我freeze_function
args...
扔到一个元组中,然后在lambda中解压缩它们,也许使用等效于std::apply
from C ++ 17)。
This can be either baked into the ThreadBoundary
by adding a signature (making it a template), or can be used to store your unrealized function calls somewhere and freezing them immediately prior to passing the resulting std::function<void()>
to ThreadBoundary
. 可以通过添加签名(使其成为模板)将其烘焙到ThreadBoundary
,也可以将未实现的函数调用存储在某个位置,并在将生成的std::function<void()>
传递给ThreadBoundary
之前立即冻结它们。 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.