6

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);
}
Uwop
  • 89
  • 1
  • 2
  • 7
  • Why do you want to do this? What if thread B was in the middle of something else? – doctorlove Apr 14 '14 at 16:09
  • ThreadB is a networking thread and it has established a TCP connection to a server and will wait for work to do. I'm trying to make it so there is an asynchronous upload while maintaining connection to the server. – Uwop Apr 14 '14 at 16:13
  • 1
    If you want to use Qt classes, read the "Multithreading Technologies in Qt" article: http://qt-project.org/doc/qt-5/threads-technologies.html -- It compares the different techniques, and gives examples of different use cases. – JKSH Apr 15 '14 at 00:30

3 Answers3

8

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.

alphashooter
  • 304
  • 1
  • 7
  • 2
    You'll need some locks this way – doctorlove Apr 14 '14 at 18:19
  • @doctorlove Of course, locks are necessary. It's just an example how it works, so I didn't suppose it would be just copied. – alphashooter Apr 14 '14 at 18:31
  • @user1569346 And you won't find it. :) It's my fault, I didn't explain this. It must be a class which transfers data between your threads, like [DTO](http://en.wikipedia.org/wiki/Data_transfer_object). It isn't declared anywhere, just my abstraction. So, it seems, you should define it by yourself or try to find some similar solution. – alphashooter Apr 14 '14 at 20:00
  • Thanks, I figured it out shortly after I asked you here. – Uwop Apr 15 '14 at 16:20
1

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.

OnWhenReady
  • 939
  • 6
  • 15
  • `__FUNCTION__` is implementation defined and it's not portable. Using the Qt5 (last/third) solution from [this answer](http://stackoverflow.com/a/21653558/1329652), the ugly macro hack can be simply written without any macros as `if (QThread::currentThread() != thread()) { postMetaCall(this, [=]{ fn(user_, pwd_); }); return; }`. – Kuba hasn't forgotten Monica Apr 15 '14 at 17:47
  • Time goes by :-) The MACRO hack is indeed quiet ugly (but worked for pre Qt5/c++11). Thanks for the posted solution. Looks nice and clean. – OnWhenReady Apr 15 '14 at 18:31
0

I got this to work in R. R has environment(). So you must use a future or promise to execute inside a different environment. I thought environments were thread dependent , not sure of the full mechanics, but a future object will execute and return the value() assigned to it.

kraggle
  • 196
  • 2
  • 9