Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a function from another thread

My application has multiple threads on startup. I need thread A to be able to force thread B to run a function(With parameters as well.) I've tried searching on google but could not find what I needed, lots of stack overflow articles that didn't seem to relate to what I was doing. I'm not sure if I'm just using the wrong term for this, and maybe that's why I can't find the information I need. I'm open to using Boost if there is an option in their threading library, and if there is, if you could point me in the direction of some sample code I could look at which does what I need. My application also uses QT already, although I have never used the QT threading lib so I don't know if it includes this either.

Basically what I need to do is this.

//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);
}
like image 910
Uwop Avatar asked Mar 20 '23 21:03

Uwop


2 Answers

There's no way to do it explicitly. You can transfer messages between your threads, e.g. using events, which would trigger calling of the method needed. But the target thread must be in loop, waiting for message. It's the only one way you can force the thread to go in another subroutin.

Abstract example:

// in ThreadB [the target thread]

std::vector<Message> messages;
bool wait_message;

// ...

while(wait_message) {
    if(messages.size() > 0) {
        // process messages
    }
}

// ...

And for another thread:

// in ThreadA

// ... 

messages.push_back(Message("call_my_method", some_data));

// ... 

Class Message (just for example):

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; }
};

For only the function calls, you can use std::function and std::bind and transfer just a callback, which provides some fixed signature (e.g. void(void)), with your message.

like image 185
alphashooter Avatar answered Apr 01 '23 14:04

alphashooter


It is possible my using the metaobject system. But be aware of the consequences. Just take a look at this blog entry (especially the MACRO part in the comments) -> click me I stole this part for a project some years ago and liked it (but you have to really understand what you are doing and how Qt is handling the call)

You have to define the following MACROs and extend the last part to your needs:

// 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);

Now you have to implement your functions as e.g.

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

As i already said: It was not my idea - credit goes to Dave Smith.

like image 34
OnWhenReady Avatar answered Apr 01 '23 14:04

OnWhenReady