bthread了解

转自:https://zhuanlan.zhihu.com/p/294129746 ,讲的很深奥,目前还看不懂。

https://github.com/apache/incubator-brpc/blob/master/docs/cn/bthread.md

https://ehds.github.io/2021/07/20/bthread_schedule/

1.介绍

bthread是brpc实现的一套“协程”,bthread是M:N的“协程”,每个bthread之间的平等的,所谓的M:N是指协程可以在线程间迁移。

M是指有M个协程,N个线程。”M:N“是指M个bthread会映射至N个pthread,一般M远大于N。

要实现M:N其中关键就是:工作窃取(Work Stealing)算法。

三大件:

  1. TaskControl:进程内全局唯一。
  2. TaskGroup:和线程数相当,每个线程(pthread)都有一个TaskGroup,也称为worker,协调 bthread 的调度。
  3. TaskMeta:表征bthread上下文的真实结构体。

bthread并不严格从属于一个pthread,但是bthread在运行的时候还是需要在一个pthread中的worker中(也即TG)被调用执行的。

2.bthread与协程 

协程特指N:1线程库,即所有的协程运行于一个系统线程中,由于不跨线程,协程之间的切换不需要系统调用,可以非常快(100ns-200ns)。但代价是协程无法高效地利用多核,代码必须非阻塞,否则所有的协程都被卡住。(意思是说,因为所有协程都是属于一个线程的,并且一个线程下只能有一个协程运行,那如果这个正在运行的协程阻塞了,其他的协程也没办法切换?) 

bthread是一个M:N线程库,一个bthread被卡住不会影响其他bthread,可以更好的利用多核。关键技术两点:

  • work stealing调度:让bthread更快地被调度到更多的核心上;
  • butex:让bthread和pthread可以相互等待和唤醒。(?什么意思?)

3.pthread运行bthread

pthread worker在任何时间只会运行一个bthread,当前bthread挂起时,

  1. pthread worker先尝试从本地runqueue弹出一个待运行的bthread,
  2. 若没有,则随机偷另一个worker的待运行bthread,
  3. 仍然没有才睡眠并会在有新的待运行bthread时被唤醒。

【上述的也就是work stealing算法吧】

4.bthread与阻塞

一个bthread阻塞会影响其他bthread吗?

不影响。

  • 若bthread因bthread API而阻塞,它会把当前pthread worker让给其他bthread。
  • 若bthread因pthread API或系统函数而阻塞,当前pthread worker上待运行的bthread会被其他空闲的pthread worker偷过去运行。

5. TaskGroup

void TaskGroup::run_main_task() {//入口
   ......
    TaskGroup* dummy = this;
    bthread_t tid;
    while (wait_task(&tid)) {//不断的 wait_task, 等到 bthread 后
        TaskGroup::sched_to(&dummy, tid);//即进入 sched_to 开始调度该 bthread
        DCHECK_EQ(this, dummy);
        DCHECK_EQ(_cur_meta->stack, _main_stack);
        if (_cur_meta->tid != _main_tid) {
            TaskGroup::task_runner(1/*skip remained*/);
        }
       ......
}

在 TaskGroup 中有两个用于存储 bthread 的队列:

WorkStealingQueue<bthread_t> _rq;
RemoteTaskQueue _remote_rq;
  •  WorkStealingQueue 用于存储由 task_group(worker) 自身生成的 bthread,
  • RemoteTaskQueue 用于存储非 task_group 生成的bthread

WorkStealingQueue 中的 push 和 pop 只会被本 worker 调用,仅与 steal 产生竞争(意思是可能有其他pthread来竞争这个队列的push和pop?),所以使用无锁实现减少锁的开销。RemoteTaskQueue 由于是由非 worker 调用,且是随机选择task_group, 所以使用锁的方式实现(这个队列存储的意思是如果他的workstealingqueue是空的话,它就去经侦这个remote队列的bthread去执行?)。

流程是:

如果本 taskgroup 的 _rq 没有任务时,就会触发 steal_task。task_group 的 steal_task 会首先从本地的 _remote_rq 取任务,如果没有则会从其他 task_group 拿任务。通过 task_control 这个管理者实现从其他 task_group 拿任务:

bool stolen = false;
size_t s = *seed;//从随机位置开始遍历
for (size_t i = 0; i < ngroup; ++i, s += offset) {//每次跨度 offset,遍历 ngroup 次
    TaskGroup* g = _groups[s % ngroup];
    // g is possibly NULL because of concurrent _destroy_group
    if (g) {
        if (g->_rq.steal(tid)) {
            stolen = true;
            break;
        }
        if (g->_remote_rq.pop(tid)) {
            stolen = true;
            break;
        }
    }
}

先从其他 task_group 的 working_stealing_queue 里面取,其次从remote_rq 取;取到任务后立即返回。

6.bthread_start_background开启bthread

bthread_start_background 相当于pthread_create,

int bthread_start_background(bthread_t* __restrict tid,
                             const bthread_attr_t* __restrict attr,
                             void * (*fn)(void*),
                             void* __restrict arg) {
    bthread::TaskGroup* g = bthread::tls_task_group;
    if (g) {
        // start from worker
        return g->start_background<false>(tid, attr, fn, arg);
    }
    return bthread::start_from_non_worker(tid, attr, fn, arg);
}

如果创建这个bthread的调用者是外部的线程(非task_group)【??外部的线程怎么理解??】,调用start_from_non_worker。start_from_non_worker的工作就是从task_control随机选择一个task_group作为这个新bthread的归宿。 bthread被加入 remote_rq。

如果创建者是已经存在于task_group的bthread则会以当前的task_group作为最终归宿。task_group中的start_background做的工作就是为新的bthread创建资源TaskMeta,然后将它加入remote_rq/_rq。

7.bthread与brpc

利用epoll监听读写事件,然后分发处理,事件监听调度EventDispatcher运行在bthread中,在brpc中读事件一般分为两种:

1.新的请求连接(OnNewConnections)2.读socket上的message (OnNewMessages)

如果是发生读事件,会抢占当前 epoll 所在 task_group 的执行权限,会调用bthread_start_ugrent来执行读事件;那么正在执行的bthread会被挂起到 _rq 队列中(可以被其他 task_group steal_task ),读事件 handler 的 btrhead 会被调度到当前task_group中(发生上下文切换 )。

 

posted @ 2022-08-28 21:52  lypbendlf  阅读(712)  评论(0编辑  收藏  举报