在不同的线程

时间:2016-10-04 16:12:08

标签: c++ multithreading c++11 lambda

我在c ++框架中工作,主要是在c ++ 11之前编写,它允许我们从一个线程向另一个线程发送事件(假设接收线程正在运行一个事件队列,所以它&# 39; s主要用于从辅助线程向主UI线程发送事件。)

目前,完成此任务的代码非常冗长 - 它要求我们定义两个类:

  1. 包含我们要传输的任何信息的事件类。
  2. 处理接收线程中事件的侦听器类。
  3. 我们最近转向了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()内的函数。所以我的问题是:

    1. 是否可以存储参数包,然后将其传递给std :: function?
    2. 有没有更好的方法在c ++ 11/14中做到这一点,并不需要进行大规模的重新设计?我们的框架目前正在包装OS功能来实现这一目标(即在Windows上它在OS X中使用SendMessage vs performSelectorOnMainThread)。

2 个答案:

答案 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()>