线程间函数调用

1 四种api调用

在libcamera中,类和线程通过Object绑定,它们的关系为:
Object通过Thread实现invokeMethod(),类继承Object,外部就可以通过类指针调用invokeMethod()了。
b->invokeMethod(B::func, Type, param...),调用类型Type有下面4中。
 

1.1 direct

直接在当前线程调用,就像调用普通函数一样。
 

1.2 queued

总是去目标线程排队调用;非阻塞,不等待调用结果。
 

1.3 auto

如果调用线程和目标线程一致,类似普通函数直接调用;
如果调用线程和目标线程不一致,排队调用,非阻塞;
 

1.4 blocking

如果调用线程和目标线程一致,类似普通函数直接调用;
如果调用线程和目标线程不一致,排队调用,等待调用结果;
 
 

2 情况分析

2.1 线程安全

如果目标对象在另一个线程,必须通过消息队列异步/同步调用
而且要做到:一个对象只在一个线程被调用,那就是创建该对象的线程!
这样设计,避免了对象的竞争访问和锁的引入,设计对象的时候也不用担心线程安全的问题,
设计逻辑清晰,更容易维护。
 

2.2 direct

invokeMethod + direct:类似直接调用b->func();
不符合线程安全原则,要避免使用。
 

2.3 queued

在跨线程的情况下,auto == queued;
如果想要异步调用,也就是只需要把消息发过去,并不关心何时执行完成,使用queued;
如果使用queued调用参数中带有指针,需要保证真正调用后,调用者才释放指针,会比较麻烦,还需要添加一个api告知调用结束。
 

2.4 blocking

在跨线程的情况下,blocking = queued + wait;
这里的queued表示也会把调用message放到队列中,wait表示直到调用完成返回值,退出调用。
非常适合stop之类的指令:既不会影响目标线程stop之前的任务,也可以保证stop之后目标线程不会继续处理新的任务。
 

3 源码赏析

3.1 使用场景

 * \enum ConnectionType
 * \brief Connection type for asynchronous communication
 *
 * This enumeration describes the possible types of asynchronous communication
 * between a sender and a receiver. It applies to Signal::emit() and
 * Object::invokeMethod().

在libcamera中,有两种跨线程调用的方法:
① Signal:emit()

// Thread A, class AA
// 绑定
AA.Signal aaSignal_;
aaSignal.connect(B::func);
// 触发
aaSignal.emit(params...);
// Thread B, class BB
// 响应
BB::func(params...)

② Object::invokeMethod()

// Thread A, class AA
AA.bb;
AA.bb->invokeMethod(type, type, params...);

 

3.2 4种connection type

 * \var ConnectionTypeAuto
 * \brief If the sender and the receiver live in the same thread,
 * ConnectionTypeDirect is used. Otherwise ConnectionTypeQueued is used.
 *
 * \var ConnectionTypeDirect
 * \brief The receiver is invoked immediately and synchronously in the sender's
 * thread.
 *
 * \var ConnectionTypeQueued
 * \brief The receiver is invoked asynchronously
 *
 * Invoke the receiver asynchronously in its thread when control returns to the
 * thread's event loop. The sender proceeds without waiting for the invocation
 * to complete.
 *
 * \var ConnectionTypeBlocking
 * \brief The receiver is invoked synchronously
 *
 * If the sender and the receiver live in the same thread, this is equivalent to
 * ConnectionTypeDirect. Otherwise, the receiver is invoked asynchronously in
 * its thread when control returns to the thread's event loop. The sender
 * blocks until the receiver signals the completion of the invocation.

 

3.3 activatePack 入参和返回值

 * \brief Invoke the bound method with packed arguments
 * \param[in] pack Packed arguments
 * \param[in] deleteMethod True to delete \a this bound method instance when
 * method invocation completes
 *
 * The bound method stores its return value, if any, in the arguments \a pack.
 * For direct and blocking invocations, this is performed synchronously, and
 * the return value contained in the pack may be used. For queued invocations,
 * the return value is stored at an undefined point of time and shall thus not
 * be used by the caller.
 *
 * \return True if the return value contained in the \a pack may be used by the
 * caller, false otherwise

异步调用不应该有返回值,因为返回值无意义;
同时异步调用传递的参数也不应该是指针,因为可能会失效!
具体参考: 线程间函数调用
 

3.4 activatePack

bool BoundMethodBase::activatePack(std::shared_ptr<BoundMethodPackBase> pack, bool deleteMethod)
{
    ConnectionType type = connectionType_;
    if (type == ConnectionTypeAuto) {
        if (Thread::current() == object_->thread())
            type = ConnectionTypeDirect;
        else
            type = ConnectionTypeQueued;
    } else if (type == ConnectionTypeBlocking) {
        if (Thread::current() == object_->thread())
            type = ConnectionTypeDirect;
    }

    switch (type) {
    case ConnectionTypeDirect:
    default:
        invokePack(pack.get());
        if (deleteMethod)
            delete this;
        return true;

    case ConnectionTypeQueued: {
        std::unique_ptr<Message> msg =
            std::make_unique<InvokeMessage>(this, pack, nullptr, deleteMethod);
        object_->postMessage(std::move(msg));
        return false;
    }

    case ConnectionTypeBlocking: {
        Semaphore semaphore;

        std::unique_ptr<Message> msg =
            std::make_unique<InvokeMessage>(this, pack, &semaphore, deleteMethod);
        object_->postMessage(std::move(msg));

        semaphore.acquire();
        return true;
    }
    }
}
posted @ 2025-12-12 13:46  moonのsun  阅读(2)  评论(0)    收藏  举报