【sylar】框架篇-Chapter6-协程调度模块

本文介绍了sylar C++高性能分布式服务器框架的设计,包括N-M的协程调度器,协程在线程间切换,以及如何解决子协程不能运行另一子协程的问题。详细阐述了Scheduler和SchedulerSwitcher类的功能,以及协程调度的实现细节,如调度线程、任务队列管理和协程上下文切换。此外,还提及了基于Scheduler实现的IOManager的实际应用。

站在巨人的肩膀上

C++高性能分布式服务器框架

从零开始重写sylar C++高性能分布式服务器框架

概述

  • 一个 N-M 的协程调度器,N 个线程运行 M 个协程,协程可以在线程之间进行切换,协程也可以绑定到指定线程运行。
  • 实现协程调度之后,可以解决前一章协程模块中子协程不能运行另一个子协程的缺陷,子协程可以通过向调度器添加调度任务的方式来运行另一个子协程。
  • 协程调度器调度的是协程,函数(可执行对象)被包装成协程。

Scheduler

  • 协程调度器类。
  • t_scheduler_fiber 保存当前线程的调度协程,加上 Fiber 模块的 t_fiber 和 t_thread_fiber,每个线程总共可以记录三个协程的上下文信息。

SchedulerSwitcher

  • 调度器切换类。

其他说明

  • 协程调度最难理解的地方是当 caller 线程也参与调度时调度协程和主线程切换的情况。
    • 调度线程可以包含 caller 线程。
    • 在非 caller 线程里,调度协程就是调度线程的主协程;但在 caller 线程里,调度协程并不是 caller 线程的主协程,而是相当于 caller 线程的子协程。
    • 在非对称协程里,子协程只能和线程主协程切换,而不能和另一个子协程切换。而这里,调度协程和任务协程,都是子协程,也就是说,调度协程不能直接和任务协程切换。sylar 的解决方案是:给每个线程增加一个线程局部变量用于保存调度协程的上下文就可以了,这样,每个线程可以同时保存三个协程的上下文,一个是当前正在执行的协程上下文,另一个是线程主协程的上下文,最后一个是调度协程的上下文。
  • 支持添加函数或协程作为调度对象,并且支持将函数或协程绑定到一个具体的线程上执行。
  • 调度协程执行 run 方法,负责从调度器的任务队列中取任务执行,取出的任务即子协程。每个子协程执行完后都必须返回调度协程,由调度协程重新从任务队列中取新的协程并执行。如果任务队列空了,那么调度协程会切换到一个 idle 协程,这个 idle 协程什么也不做,等有新任务进来前,不断地与调度协程进行切换(这里其实是忙等)。
  • 如果任务队列为空,那么在添加任务之后,要调用一次 tickle 方法以通知各调度线程的调度协程有新任务来了。
  • 调度器的停止行为要分两种情况讨论,首先是 use_caller 为 false 的情况,这种情况下,由于没有使用 caller 线程进行调度,那么只需要简单地等各个调度线程的调度协程退出就行了。如果 use_caller 为 true,表示 caller 线程也参与了调度,这时,调度器初始化时记录的属于 caller 线程的调度协程就要起作用了,在调度器停止前,应该让这个 caller 线程的调度协程也运行一次,让 caller 线程完成调度工作后再退出。如果调度器只使用了 caller 线程进行调度,那么所有的调度任务要在调度器停止时才会被调度(因为只使用了 caller 线程进行调度的话,就意味着用的是 caller 线程的子协程进行调度,而只有在调度器停止时,该子协程才会被 call())。
  • sylar 的协程调度模块因为存任务队列空闲时调度线程忙等待的问题,所以实际上并不实用,真正实用的是后面基于 Scheduler 实现的 IOManager。

部分相关代码

/**
 * @filename    scheduler.h
 * @brief   协程调度模块
 * @author  L-ge
 * @version 0.1
 * @modify  2022-06-29
 */
#ifndef __SYLAR_SCHEDULER_H__
#define __SYLAR_SCHEDULER_H__

#include <memory>
#include <vector>
#include <list>
#include <iostream>
#include "fiber.h"
#include "thread.h"

namespace sylar
{

/**
 * @brief   协程调度器
 */
class Scheduler
{
public:
    typedef std::shared_ptr<Scheduler> ptr;
    typedef Mutex MutexType;

    /**
     * @brief   构造函数
     *
     * @param   threads     线程数量
     * @param   use_caller  是否使用当前调用线程
     * @param   name        协程调度器名称
     */
    Scheduler(size_t threads = 1, bool use_caller = true, const std::string& name = "");
    virtual ~Scheduler();

    const std::string& getName() const { return m_name; }
    
    static Scheduler* GetThis();

    /**
     * @brief   返回当前协程调度器的调度协程(不一定是主协程)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值