从另一个线程调用函数

Call a function from another thread

本文关键字:函数 调用 线程 另一个      更新时间:2023-10-16

我的应用程序在启动时有多个线程。我需要线程A能够强制线程B运行一个函数(也有参数。)我试过在谷歌上搜索,但找不到我需要的东西,很多堆栈溢出的文章似乎与我正在做的事情无关。我不确定我是否只是用错了这个词,也许这就是我找不到我需要的信息的原因。如果他们的线程库中有一个选项,我愿意使用Boost,如果有,如果你能给我指明一些示例代码的方向,我可以看看它能满足我的需求。我的应用程序也已经使用了QT,尽管我从未使用过QT线程库,所以我也不知道它是否包括这一点。

基本上我需要做的就是这个。

//Thread A
void ThreadA()
{
   //Do work to get data
   //Pass Data to another function, and have thread B run it.
   ThreadB.Run(data,arg2,arg3);
}

没有办法显式执行。您可以在线程之间传输消息,例如使用事件,这将触发对所需方法的调用。但目标线程必须处于循环中,等待消息。这是唯一一种可以强制线程进入另一个子例程的方法。

摘要示例:

// in ThreadB [the target thread]
std::vector<Message> messages;
bool wait_message;
// ...
while(wait_message) {
    if(messages.size() > 0) {
        // process messages
    }
}
// ...

对于另一个线程:

// in ThreadA
// ... 
messages.push_back(Message("call_my_method", some_data));
// ... 

类别Message(仅作为示例):

class Message {
private:
    std::string _what;
    void* _data;
public:
    Message(const std::string& what, void* data) : _what(what), _data(data) { }
    Message(const Message& inst) : _what(inst._what), _data(inst._data) { }
    const std::string& what() const { return _what; }
    void* data() const { return _data; }
};

仅对于函数调用,您可以使用std::functionstd::bind,并在消息中只传递一个回调,该回调提供一些固定签名(例如void(void))。

我有可能使用元对象系统。但要注意后果。只要看看这个博客条目(尤其是评论中的MACRO部分)->点击我几年前,我为一个项目偷了这个部分,并喜欢它(但你必须真正了解你在做什么,以及Qt是如何处理呼叫的)

您必须定义以下MACRO,并将最后一部分扩展到您的需求:

// HELPER MACROS (from
// Just a helper macro:
#define NO_RETURN_VALUE
// This does the real work:
#define THREAD_MAGIC(targetThread, returnValue, args)     
if(QThread::currentThread() != targetThread->thread())                     
{                                                                
    QString slotName = __FUNCTION__;                             
    slotName.remove(QRegExp("^.*::"));                           
    bool ret = metaObject()->invokeMethod(this,                  
            qPrintable(slotName), Qt::QueuedConnection,          
            args.count() >=  1 ? args[0] : QGenericArgument(0),  
            args.count() >=  2 ? args[1] : QGenericArgument(0),  
            args.count() >=  3 ? args[2] : QGenericArgument(0),  
            args.count() >=  4 ? args[3] : QGenericArgument(0),  
            args.count() >=  5 ? args[4] : QGenericArgument(0),  
            args.count() >=  6 ? args[5] : QGenericArgument(0),  
            args.count() >=  7 ? args[6] : QGenericArgument(0),  
            args.count() >=  8 ? args[7] : QGenericArgument(0),  
            args.count() >=  9 ? args[8] : QGenericArgument(0),  
            args.count() >= 10 ? args[9] : QGenericArgument(0)); 
    if(!ret)                                                     
    {                                                            
        qFatal(qPrintable(__FUNCTION__ +                         
          QString(" Could not call QMetaObject::invokeMethod(). " 
          "Check your argument list quantity and types.")));     
    }                                                            
    return returnValue;                                          
 }
#define MAKE_THREAD_SAFE_0(TargetThread, returnValue)                
    do {                                                         
    QList<QGenericArgument> args;                                
    THREAD_MAGIC(TargetThread, returnValue, args);    
    } while (0);                                                  

   #define THREAD_MAGIC_1(TargetThread, returnValue, ArgType1, ArgName1)         
    do {                                                         
    QList<QGenericArgument> args = QList<QGenericArgument>() <<  
        Q_ARG(ArgType1, ArgName1);                               
    THREAD_MAGIC(TargetThread, returnValue, args);    
    } while (0);
                                                   
#define THREAD_MAGIC_2(TargetThread, returnValue, ArgType1, ArgName1, ArgType2, ArgName2) 
    do {                                                         
    QList<QGenericArgument> args = QList<QGenericArgument>() <<  
        Q_ARG(ArgType1, ArgName1) <<                             
        Q_ARG(ArgType2, ArgName2);                               
    THREAD_MAGIC(TargetThread, returnValue, args);    
    } while (0);

现在你必须实现你的功能,例如

void ThreadClass::fn(const QString& user_, const QString& pwd_)
{
  THREAD_MAGIC_2(this, NO_RETURN_VALUE, QString, user_, QString, pwd_);
  // ... implementation of the function
}

正如我已经说过的:这不是我的主意——这要归功于戴夫·史密斯。

我在R.R has environment()中实现了这一点。因此,您必须使用未来或承诺在不同的环境中执行。我认为环境是依赖于线程的,不确定完整的机制,但未来的对象将执行并返回分配给它的值()。