(六)线程回调

上一章的结尾已经实现了定时函数功能,但对于多线程同步依旧不行,比如线程A 调用线程B的Loop,想注册线程B中的回调函数;

1. In Loop

Muduo 在线程间调配任务会把其转移至IO 线程,因此可以不用锁保证线程安全;

void EventLoop::runInLoop(const Functor &cb) {
    if (isInLoopThread()) {
        cb();
    } else {
        queueInLoop(cb);
    }
}

但IO 线程通常阻塞在poll 调用,因此使用eventfd 增加新Channel 从而高效唤醒,使IO 线程立即执行用户回调;

static int createEventfd() {
    int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (evtfd < 0) {
        LOG_SYSERR << "Failed in eventfd";
        abort();
    }
    return evtfd;
}	

创建eventfd 后设置为wakeupFd,并注册通道:

    _wakeupChannel->setReadCallback(std::bind(&EventLoop::handleRead, this));
    _wakeupChannel->enableReading();

将回调函数添加到等待队列:

void EventLoop::queueInLoop(const Functor &cb) {
    {
        MutexLockGuard lock(_mutex);
        _pendingFunctors.push_back(cb);
    }
    if (!isInLoopThread() || _callPendingFunctors) {
        wakeup();
    }
}

因为pendingFunctors 会暴露给其他线程,因此需要添加锁({} 为了缩小临界区);

而wakeup 函数通过向eventfd 发送一个字节,唤醒位于poll 阻塞的IO 线程(单线程就没有这样的烦恼啦:D),唤醒后的处理函数就是简单地接受这个字节;

第二个唤醒条件表示当前正在进行pending 函数的处理, 即在IO 线程也需要wakeup,因为Functor 有可能会再次调用queueInLoop,以便新加的cb 可以被及时调用;(在pendingFunctors 的处理中若再次调用到了queueInLoop,cb回调 在当前pending 所有的回调处理完后继续Loop,若不wakeup 会等poll,wakeup 后poll 可以拿着eventfd 立即返回并再次处理active 和 pending)

2. Safe Timer Thread

存在线程A 在线程B 添加定时回调任务的可能,因此定时器需要保证线程安全;

TimerId TimerQueue::addTimer(const TimerCallback &cb, Timestamp when,
                             double interval) {
    Timer *timer = new Timer(cb, when, interval);
    _loop->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));

    return TimerId(timer);
}

void TimerQueue::addTimerInLoop(Timer *timer) {
    _loop->assertInLoopThread();
    bool earliestChanged = insert(timer);

    if (earliestChanged) {
        resetTimerfd(_timerfd, timer->expiration());
    }
}

拆分函数即可做到;

3. EventLoopThread

IO 线程不一定使主线程,一个程序也不止一个IO 线程;

EventLoop *EventLoopThread::startLoop() {
    assert(!_thread.started());
    _thread.start();

    {
        MutexLockGuard lock(_mutex);
        LOG_TRACE << "lock in startLoop is ok";
        while (_loop == NULL) {
            _cond.wait();
        }
    }

    return _loop;
}

void EventLoopThread::threadFunc() {
    EventLoop loop;

    {
        MutexLockGuard lock(_mutex);
        _loop = &loop;
        _cond.notify();
    }

    loop.loop();
    // assert(exiting_);
}

这儿的Mutex 和 Condition 想换成C++11 标准库的condition_variable 和 unique_lock(mutex)
但总会产生resource dead lock avoided,应该是锁的问题,但我仔细梳理一下逻辑感觉也没有问题QAQ;换成POSIX 接口就没问题了;

关于标准库真的搞不懂一些设计,thread 没有给start 控制开启,condition_variable wait 必须要给unique_lock 作为参数,还有设计的比较玄学的时间库chrono…

记录一下!

Mutex.h

class MutexLock : boost::noncopyable {
  private:
    std::mutex _mutex;
    std::unique_lock<std::mutex> _lock;
    pid_t _holder;
    /* data */
  public:
    MutexLock() : _holder(0), _lock(_mutex) {}
    ~MutexLock() { assert(_holder == 0); }
    bool isLockThisThread() const {
        return TinyMuduo::CurrentThread::tid() == _holder;
    }
    bool assertIsLocked() const { assert(isLockThisThread()); }

    std::unique_lock<std::mutex> &getPthreadMutex() { return _lock; }

    void lock() {
        _lock.lock();
        _holder = TinyMuduo::CurrentThread::tid();
    }
    void unlock() {
        _lock.unlock();
        _holder = 0;
    }
};

class MutexLockGuard : boost::noncopyable {
  private:
    MutexLock &_lock;

  public:
    MutexLockGuard(MutexLock &mutex) : _lock(mutex) { _lock.lock(); }
    ~MutexLockGuard() { _lock.unlock(); }
};

Condition.h:

class Condition : boost::noncopyable {
  public:
    explicit Condition(MutexLock &mutex) : _mutex(mutex) {}

    ~Condition() {}

    void wait() { _cv.wait(_mutex.getPthreadMutex()); }

    // returns true if time out, false otherwise.
    bool waitForSeconds(int seconds) {

        return _cv.wait_for(_mutex.getPthreadMutex(),
                            std::chrono::seconds(seconds)) ==
               std::cv_status::timeout;
    }

    void notify() { _cv.notify_one(); }

    void notifyAll() { _cv.notify_all(); }

  private:
    std::condition_variable _cv;
    MutexLock &_mutex;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值