当前位置: 首页 > news >正文

四川关于工程建设网站硬盘做网站空间

四川关于工程建设网站,硬盘做网站空间,成都服务器维护,在线简历制作什么是协程和协程调度#xff1f; 基本概念 协程 协程是一种比线程更轻量级的并发编程结构#xff0c;它允许在函数执行过程中暂停和恢复执行状态#xff0c;从而实现非阻塞式编程。协程又被称为用户级线程#xff0c;这是由于协程包括上下文切换在内的全部执行逻辑都是…什么是协程和协程调度 基本概念 协程 协程是一种比线程更轻量级的并发编程结构它允许在函数执行过程中暂停和恢复执行状态从而实现非阻塞式编程。协程又被称为用户级线程这是由于协程包括上下文切换在内的全部执行逻辑都是由程序员显式指定的操作系统并不知道协程的存在。 协程类的实现 /* Fiber类的声明 主要作用1. 提供了一些基础功能来管理和调度协程的执行 */ class Fiber : public std::enable_shared_from_thisFiber { // std::enable_shared_from_thisT C标准库模板类用于启用从类的成员函数生成共享指针的功能 public:typedef std::shared_ptrFiber ptr; // 指向Fiber类对象的共享指针// Fiber状态机enum State {READY, // 就绪态刚创建或者从yield中恢复准备运行RUNNING, // 运行态协程正在运行TERM, // 结束态协程的回调函数执行完毕已经结束};private:// 私有的默认构造函数用来在GetThis中调用以初始化主协程不能显式调用Fiber();public:// 含参构造函数用于构造子协程指定协程的回调函数和栈大小Fiber(std::functionvoid() cb, size_t stackSz 0, bool run_in_scheduler true); ~Fiber();void reset(std::functionvoid() cb); // 重置协程状态复用栈空间。给协程重新绑定一个回调函数在同一个空间但是功能改变了void resume(); // 切换协程到运行态void yield(); // 让出协程执行权uint64_t getId() const { return id_; } // 获得协程IDState getState() const { return state_; } // 活动协程状态// 静态成员函数static void SetThis(Fiber *f); // 设置当前正在运行的协程static Fiber::ptr GetThis(); // 获取当前线程中的执行协程如果当前线程没有创建协程则创建一个且作为主协程static uint64_t TotalFiberNum(); // 获得协程总数static void MainFunc(); // 协程执行函数static uint64_t GetCurFiberID(); // 获得当前协程IDprivate:uint64_t id_ 0; // 协程IDuint32_t stackSize_ 0; // 协程栈大小State state_ READY; // 协程状态ucontext_t ctx_; // 协程的上下文void *stack_ptr nullptr; // 协程栈地址std::functionvoid() cb_; // 协程回调函数bool isRunInscheduler_; // 本协程是否参与调度器调度 };上述协程的封装提供了一些基础功能使得协程可以被创建、调度和切换。协程共有三种状态 READY就绪态协程刚创建或从yield中恢复准备运行。RUNNING运行态协程正在运行。TERM结束态协程的回调函数执行完毕已经结束。 简单来说协程可以看作是对要执行函数cb_的一个封装但是加入了状态、上下文信息等内容能够在多线程中实现异步执行。 协程的优势 协程不是操作系统的底层特性系统感知不到它的存在。它运行在线程里面通过分时复用线程的方式运行不会增加线程的数量。协程也有上下文切换但是不会切换到内核态去比线程切换的开销要小很多。每个协程的体积比线程要小得多一个线程可以容纳数量相当可观的协程。 在IO密集型的任务中有着大量的阻塞等待过程协程采用协作式调度在IO阻塞的时候让出CPU当IO就绪后再主动占用CPU通过回调函数牺牲任务执行的公平性换取吞吐量。 事物都有两面性协程也存在几个弊端 线程可以在多核CPU上并行无法将一个线程的多个协程分摊到多核上。协程执行中不能有阻塞操作否则整个线程被阻塞。不阻塞而是让出执行权协程的控制权由用户态决定可能执行恶意的代码。 协程调度 协程调度是一种用于管理协程执行的机制负责在多个协程之间分配执行时间使得它们能够高效地并发运行。 与线程不同协程的调度是需要由程序员显式的实现。一般来说任务队列中有很多任务协程用户手动调用协程不够灵活因此利用调度器将任务队列中的各个任务协程分配给各个线程中进行执行这就是协程调度。 于是需要先创建一个**协程调度器**然后把这些要调度的协程传递给调度器由调度器负责把这些协程一个一个消耗掉。在单线程模式下调度器会非常简单只需要实现加入任务逐个提取出任务执行即可 /*** file simple_fiber_scheduler.cc* brief 一个简单的协程调度器实现* version 0.1* date 2021-07-10*/#include sylar/sylar.h/*** brief 简单协程调度类支持添加调度任务以及运行调度任务*/ class Scheduler { public:/*** brief 添加协程调度任务*/void schedule(sylar::Fiber::ptr task) {m_tasks.push_back(task);}/*** brief 执行调度任务*/void run() {sylar::Fiber::ptr task;auto it m_tasks.begin();while(it ! m_tasks.end()) { // 挨个把协程拿出来执行task *it;m_tasks.erase(it);task-resume();}} private:/// 任务队列std::listsylar::Fiber::ptr m_tasks; };void test_fiber(int i) {std::cout hello world i std::endl; }int main() {/// 初始化当前线程的主协程sylar::Fiber::GetThis();/// 创建调度器Scheduler sc;/// 添加调度任务for(auto i 0; i 10; i) {sylar::Fiber::ptr fiber(new sylar::Fiber(std::bind(test_fiber, i)));sc.schedule(fiber);}/// 执行调度任务sc.run();return 0; }这是由于一个线程同一时刻只能运行一个协程所以挨个执行即可。然而在实际使用中势必要用到多线程来提高调度的效率。 读取任务在多线程的情况下可以简单地认为调度器创建后内部首先会创建一个调度线程池调度开始后所有调度线程从任务队列里取任务执行调度线程数越多能够同时调度的任务也就越多当所有任务都调度完后调度线程就停下来等新的任务进来。添加任务添加调度任务的本质就是往调度器的任务队列里塞任务但是只添加调度任务是不够的还应该有一种方式用于通知调度线程有新的任务加进来了因为调度线程并不一定知道有新任务进来了。当然调度线程也可以不停地轮询有没有新任务但是这样CPU占用率会很高。调度器的停止调度器应该支持停止调度的功能以便回收调度线程的资源只有当所有的调度线程都结束后调度器才算真正停止。 调度器概念的简单总结 调度器内部维护一个任务队列和一个调度线程池。开始调度后线程池从任务队列里按顺序取任务执行。调度线程可以包含caller线程。当全部任务都执行完了线程池停止调度等新的任务进来。添加新任务后通知线程池有新的任务进来了线程池重新开始运行调度。停止调度时各调度线程退出调度器停止工作。 任务和任务队列 SchedulerTask 任务可以是一个协程也可以是一个函数可以将他们封装到一个类SchedulerTask中如下 /* SchedulerTask调度任务类 主要作用表示一个调度任务这个任务可以是一个协程对象或者一个函数对象 */ class SchedulerTask { public:friend class Scheduler;SchedulerTask() { thread_ -1; // 初始化为-1}SchedulerTask(Fiber::ptr f, int t) { // 含参构造函数调度任务为协程对象fiber_ f;thread_ t;}SchedulerTask(std::functionvoid() f, int t) { // 含参构造函数调度任务为函数对象cb_ f;thread_ t;}// 清空任务void reset() {fiber_ nullptr;cb_ nullptr;thread_ -1;}private:Fiber::ptr fiber_; // 指向协程对象的指针std::functionvoid() cb_; // 回调函数int thread_; // 执行该任务的线程ID-1表示不指定线程 };任务队列则是用来存放多个任务具体实现为将任务存储到链表中std::listSchedulerTask tasks_ 每次取任务时从链表头部开始遍历寻找可以运行的任务每次添加任务的时候将任务添加到链表尾部。所以调度策略是先来先服务 多线程下的协程调度器 Scheduler 调度器的作用就是从任务队列中取出一个任务然后交给线程中的任务协程处理。实现中Scheduler类的声明如下 /* SchedulerN-M协程调度器管理多线程和协程 主要作用用于管理多个线程和协程 */ class Scheduler { public:typedef std::shared_ptrScheduler ptr; // Scheduler::ptr 是Scheduler类对象的共享指针类型的别名Scheduler(size_t threads 1, bool user_caller true, const std::string name Scheduler); virtual ~Scheduler(); // 析构函数定义为虚函数const std::string getName() const { return name_; }// 静态成员函数static Scheduler *GetThis(); // 获得当前线程的协程调度器static Fiber *GetMainFiber(); // 获得当前线程的主协程// 添加任务到调度器中template class TaskTypevoid scheduler(TaskType task, int thread -1) {bool isNeedTickle false; // 是否需要唤醒空闲的协程{Mutex::Lock lock(mutex_); // 初始化局部互斥锁lock此时会自动加锁ScopedLockImpl类中的定义// 因此这里已经加了锁下面添加调度任务就不需要在加锁了isNeedTickle schedulerNoLock(task, thread); // 添加调度任务}if (isNeedTickle) {tickle();}}void start(); // 启动调度器void stop(); // 停止调度器等待所有任务结束protected:virtual void tickle(); // 通知调度器任务到达void run(); // 协程调度函数virtual void idle(); // 无任务时执行idle协程virtual bool stopping(); // 返回是否可以停止void setThis(); // 设置当前线程调度器bool isHasIdleThreads() { // 有没有空闲进程return idleTreadCnt_ 0;}private:// 满足无锁条件时确保task_没被其他线程加锁占用添加调度任务// TODO加入使用clang的锁检查template class TaskTypebool schedulerNoLock(TaskType t, int thread) {bool isNeedTickle tasks_.empty(); // 任务队列是空可以唤醒空闲的协程SchedulerTask task(t, thread);if (task.fiber_ || task.cb_) { // 要么是协程要么是函数对象tasks.push_back(task); // 任务有效加入到任务队列中}return isNeedTickle;}std::string name_; // 调度器名称Mutex mutex_; // 互斥锁std::vectorThread::ptr threadPool_; // 调度线程池std::listSchedulerTask tasks_; // 任务队列std::vectorint threadIds_; // 线程池ID数组size_t threadCnt_ 0; // 工作线程数量不包含主线程std::atomicsize_t activeThreadCnt_ {0}; // 活跃线程数目std::atomicsize_t idleTreadCnt_ {0}; // IDL线程在线程池中处于空闲的线程数目Fiber::ptr rootFiber_; // 指向本线程的调度协程主协程bool isUseCaller_; // 是否使用use_caller模式bool isStopped_; // 线程池或调度器是否已经停止// 以下变量仅在use_caller模式中有用Fiber::ptr fiber_; // 存储调度器协程所在线程主线程的协程因为use_caller模式主线程也会执行除管理线程池或调度器以外的任务int rootThread_ 0; // 存储调度器协程所在的线程主线程ID};初始化 具体实现中协程调度器在初始化时支持传入线程数和一个布尔型的use_caller参数表示是否使用caller线程。在使用caller线程的情况下线程数自动减一并且调度器内部会初始化一个属于caller线程的调度协程并保存起来比如在main函数中创建的调度器如果use_caller为true那调度器会初始化一个属于main函数线程的调度协程。 调度器创建好后即可调用调度器的schedule方法向调度器添加调度任务但此时调度器并不会立刻执行这些任务而是将它们保存到内部的一个任务队列中。 开始调度 调用start方法启动调度后调度器会创建调度线程池线程数量由初始化时的线程数和use_caller确定。调度线程一旦创建就会立刻从任务队列里取任务执行。所有的调度线程都绑定了run方法负责从调度器的任务队列中取任务执行**即调度线程的主协程也是调度协程。从任务队列中取出的任务就是任务协程子协程**每个子协程执行完后都必须返回调度协程由调度协程重新从任务队列中取新的协程并执行。如果任务队列空了那么调度协程会切换到一个idle协程这个idle协程什么也不做等有新任务进来时idle协程才会退出并回到调度协程重新开始下一轮调度。 添加调度任务 添加调度任务对应schedule方法这个方法支持传入协程或函数并且支持一个线程号参数表示是否将这个协程或函数绑定到一个具体的线程上执行。如果任务队列为空那么在添加任务之后要调用一次tickle方法以通知各调度线程的调度协程有新任务来了。在执行调度任务时还可以通过调度器的GetThis()方法获取到当前调度器再通过schedule方法继续添加新的任务这就变相实现了在子协程中创建并运行新的子协程的功能。在调度线程的任务协程中也可以添加任务 调度器的停止 调度器的停止行为要分两种情况讨论 use_caller模式 caller线程为调度器所在的线程主线程如果use_callertrue则表示将调度器所在的线程也用于任务调度这样在实现相同调度能力的情况下指能够同时调度的协程数量线程数越少线程切换的开销也就越小效率更高一些。使用caller线程进行调度就会少开一个线程是否使用caller线程对应的处理方式也不同这里也是比较难理解的地方。 线程创建、线程内创建协程的理解 caller线程就相当于直接运行main函数得到的线程主线程非caller线程是通过new Thread的方式创建线程的。对于caller线程创建协程时直接调用GetThis()方法即可。 // caller线程 if (use_caller) { // use_caller模式当前线程也作为被调度的线程std::cout LOG_HEAD current thread as called thread std::endl;--threads; // 工作线程总数-1工作线程不包括主线程但主线程占了一个位置// 初始化caller线程的主协程Fiber::GetThis(); // 然后Fiber::cur_thread_fiber即可获取初始化后的主协程std::cout LOG_HEAD init caller threads main fiber success std::endl;对于非caller线程也就是线程池里的线程需要先创建线程绑定run方法然后在run方法中创建主协程 // 非caller线程的创建和协程初始化 for (size_t i 0; i threadCnt_; i) {threadPool_[i].reset(new Thread(std::bind(Scheduler::run, this), name_ _ std::to_string(i))); // 创建非caller线程并执行threadIds_.push_back(threadPool_[i]-getId()); }void Scheduler::run() {std::cout LOG_HEAD begin run std::endl;set_hook_enable(true); // TODOsetThis(); if (GetThreadId() ! rootThread_) { // 当前线程不是caller线程因为caller线程的主协程调度协程已经初始化过了)// 初始化主协程cur_scheduler_fiber Fiber::GetThis().get();}... ...... ... }任务协程、调度协程、主协程的概念 任务协程就是我们要运行的任务可以理解为任务队列中的单个任务。 调度协程cur_scheduler_fiber在每一个线程中都会有一个调度协程调度协程负责从任务队列中取任务然后调度协程让出执行权运行任务协程运行结束后再回到调度协程如下图所示 主协程cur_thread_fiber在之前的协程模块中我们已经知道对于每一个线程都有一个主协程主协程用于和任务协程子协程进行切换因为实现的是非对称协程只能通过主协程和任务协程子协程进行切换是否使用caller协程对应主协程任务也不同。 疑问一调度协程和主协程不是一个东西吗为什么非要再声明一个调度协程直接用主协程不就行了吗 疑问二调度协程要和任务协程子协程切换另外只能通过主协程和任务协程子协程进行切换所以调度协程就是主协程 由于调度协程负责从任务队列中取任务这就意味着我必然要给调度协程设置一个执行函数。 在协程类设计时主协程创建时并没有给主协程分配栈也没有给主协程设置执行函数具体可见Fiber()构造函数只用于协程之间的切换所以就导致主协程不能当作调度协程因为调度协程需要绑定执行run方法不断从任务队列中取任务而主协程又不能设置执行函数。所以概念上调度协程和主协程不是一个东西概念上。 idle协程空闲协程 假设当线程A没有任务可以做且整个协程调度还没结束就是说虽然线程A没任务了但是其他的线程还有任务正在执行调度还没结束此时会让线程A执行idle协程也是一个子协程idle协程内部循环判断协程调度是否停止。 如果未停止则将idle协程置为HOLD状态让出执行权继续运行run方法内的while循环从任务队列取任务。属于忙等状态CPU占用率爆炸 如果已经停止则idle协程执行完毕将idle协程状态置为TERM协程调度结束。 caller线程和非caller线程的实现区别 caller线程创建调度器的线程我们在main函数创建了调度器该caller线程的执行函数就是main函数如果想使用caller线程进行协程调度那就需要创建一个调度协程rootFiber_且绑定执行run方法才能参与协程调度。 // 创建调度协程rootFiber_其任务为执行Scheduler的run方法rootFiber_.reset(new Fiber(std::bind(Scheduler::run, this), 0, false)); // reset为shared_ptr的方法替换指针托管的对象std::cout LOG_HEAD init caller threads caller fiber success std::endl;非caller线程在创建完调度器调用start方法时会创建线程池线程池里的线程就是非caller线程每个非caller线程指定执行函数为run。注意这里run方法绑定到了线程上了也就是主协程在线程上执行run方法不断从任务队列取任务所以就不用再创建一个调度协程绑定执行run方法了 /* start启动调度器提供给外部的接口 主要功能启动调度器创建并初始化线程池使调度器能够开始执行调度任务 */ void Scheduler::start() {std::cout LOG_HEAD scheduler start std::endl;// 添加局部互斥锁保护共享资源Mutex::Lock lock(mutex_);// 检查调度器是否已经停止如果停止则直接返回if (isStopped_) {std::cout scheduler is stopped std::endl;return;}// 确保线程池应该是空的CondPanic(threadPool_.empty(), thread pool should be empty);threadPool_.resize(threadCnt_); // 根据线程数量确定线程池的大小// 创建线程加入到线程池for (size_t i 0; i threadCnt_; i) {threadPool_[i].reset(new Thread(std::bind(Scheduler::run, this), name_ _ std::to_string(i))); // 创建线程并执行threadIds_.push_back(threadPool_[i]-getId());} }总结就是 对于caller线程run方法绑定到了一个子协程上就是调度协程而主协程没有执行函数和栈空间用于协程切换主协程 ≠ 调度协程。 对于非caller线程run方法绑定到了线程上不断从任务队列寻找任务的事情交给了线程本身即主协程所以就不需要调度协程。在具体实现中为了复用代码调度协程指针cur_scheduler_fiber指向了主协程也就是主协程 调度协程。 协程调度的整体流程 注意非caller线程中没有调度协程主协程就是调度协程线程主协程本身绑定了run函数负责取任务。 协程的切换问题 无论是caller线程还是非caller线程。分两种典型情况来讨论一下调度协程的切换情况其他情况可以看成以下两种情况的组合原理是一样的。 线程数为1且use_caller为true则为caller线程对应只使用main函数线程进行协程调度的情况。 线程数为1且use_caller为false对应额外创建一个线程因为主线程不参与调度进行协程调度、main函数线程不参与调度的情况。 情况2比较好理解因为有单独的线程用于协程调度那只需要让新线程的入口函数作为调度协程从任务队列里取任务执行就行了main函数与调度协程完全不相关main函数只需要向调度器添加任务然后在适当的时机停止调度器即可。当调度器停止时main函数要等待调度线程结束后再退出参考下面的图示 情况1则比较复杂因为没有额外的线程进行协程调度那只能用main函数所在的线程来进行调度而梳理一下main函数线程要运行的协程会发现有以下三类协程 main函数对应的主协程调度协程待调度的任务协程 也就是说main函数先攒下一波协程添加调度任务然后切到调度协程里去执行等把这些协程都消耗完后再从调度协程切回来。此时线程需要保存三个协程的上下文主协程、调度协程、子协程否则会出现线程主协程跑飞的情况切换不回主协程。 拓展 多线程且use_caller为true主线程caller线程参与调度同时还会创建额外的线程进行协程调度。即为**【协程调度的整体流程】图中**展示的情况。多线程且use_caller为false主线程caller线程不参与调度会创建多个线程进行协程调度caller线程负责启动和管理这些调度线程但不参与实际的协程执行。同时也会创建专门的调度线程承担从任务队列中取出任务的工作也就是新建立的这个调度线程承担了在use_caller模式中caller线程执行任务的职责。 一些问题 1. 调度器的退出问题 调度器内部有一个协程任务队列调度器调度的实质就是内部的线程池从这个任务队列拿任务并执行那么停止调度时如果任务队列还有任务剩余要怎么处理这里可以简化处理强制规定只有所有的任务都完成调度时调度器才可以退出如果有一个任务没有执行完那调度器就不能退出。 2. 任务协程执行过程中主动调用yield让出了执行权调度器要怎么处理 半路yield的协程显然并没有执行完一种处理方法是调度器来帮协程擦屁股在检测到协程从resume返回时如果状态仍为READY那么就把协程重新扔回任务列使其可以再次被调度这样保证一个协程可以执行结束。但这种策略是画蛇添足的从生活经验的角度来看一个成熟的协程肯定要学会自我管理既然你自己yield了那么你就应该自己管理好自己而不是让别人来帮你这样才算是一个成熟的协程。对于主动yield的协程我们的策略是调度器直接认为这个任务已经调度完了不再将其加入任务队列。如果协程想完整地运行那么在yield之前协程必须先把自己再扔回当前调度器的任务队列里然后再执行yield这样才能确保后面还会再来调度这个协程。 这里规定了一点协程在主动执行yield前必须先将自己重新添加到调度器的任务队列中。如果协程不顾后果地执行yield最后的后果就是协程将永远无法再被执行也就是所说的逃逸状态。sylar的处理方法比较折衷一些sylar定义了两种yield操作一种是yield to ready这种yield调度器会再次将协程加入任务队列并等待调度另一种是yield to hold这种yield调度器不会再将协程加入任务队列协程在yield之前必须自己先将自己加入到协程的调度队列中否则协程就处于逃逸状态。再说一点sylar定义的yield to ready在整个sylar框架内一次都没用到看来sylar也同意一个成熟的协程要学会自我管理。 3. 只使用调度器所在的线程进行调度的场景 这种场景下可以认为是main函数先攒下一波协程然后切到调度协程把这些协程消耗完后再从调度协程切回main函数协程。每个协程在运行时也可以继续创建新的协程并加入调度。 4. idle如何处理也就是当调度器没有协程可调度时调度协程该怎么办 直觉上来看这里应该有一些同步手段比如没有调度任务时调度协程阻塞住比如阻塞在一个idle协程上等待新任务加入后退出idle协程恢复调度。然而这种方案是无法实现的因为每个线程同一时间只能有一个协程在执行如果调度线程阻塞在idle协程上那么除非idle协程自行让出执行权否则其他的协程都得不到执行这里就造成了一个先有鸡还是先有蛋的问题只有创建新任务idle协程才会退出只有idle协程退出才能创建新任务。 为了解决这个问题sylar采取了一个简单粗暴的办法如果任务队列空了调度协程会不停地检测任务队列看有没有新任务俗称忙等待CPU使用率爆表。这点可以从sylar的源码上发现一是Scheduler的tickle函数什么也不做因为根本不需要通知调度线程是否有新任务二是idle协程在协程调度器未停止的情况下只会yield to hold而调度协程又会将idle协程重新swapIn相当于idle啥也不做直接返回。这个问题在sylar框架内无解只有一种方法可以规避掉那就是设置autostop标志这个标志会使得调度器在调度完所有任务后自动退出。在后续的IOManager中上面的问题会得到一定的改善并且tickle和idle可以实现得更加巧妙一些以应对IO事件。 5. 有main函数线程参与调度时的调度执行时机 前面说过当只有main函数线程参与调度时可以认为是主线程先攒下一波协程然后切到调度协程开始调度这些协程等所有的协程都调度完了调度协程进idle状态这个状态下调度器只能执行忙等待啥也做不了。这也就是说主线程main函数一旦开启了协程调度就无法回头了位于开始调度点之后的代码都执行不到。对于这个问题sylar把调度器的开始点放在了stop方法中也就是调度开始即结束干完活就下班。IOManager也是类似除了可以调用stop方法外IOManager类的析构函数也有一个stop方法可以保证所有的任务都会被调度到。 也就是如果只使用caller线程进行调度那所有的任务协程都在stop之后排队调度。 6. 额外创建了调度线程时的调度执行时机 如果不额外创建线程也就是线程数为1并且use caller那所有的调度任务都在stop()时才会进行调度。但如果额外创建了线程那么在添加完调度任务之后任务马上就可以在另一个线程中调度执行。归纳起来如果只使用caller线程进行调度那所有的任务协程都在stop之后排队调度如果有额外线程那任务协程在刚添加到任务队列时就可以得到调度。 7. 协程中的异常要怎么处理子协程抛出了异常该怎么办 这点其实非常好办类比一下线程即可你会在线程外面处理线程抛出的异常吗答案是不会所以协程抛出的异常我们也不处理直接让程序按默认的处理方式来处理即可。一个成熟的协程应该自己处理掉自己的异常而不是让调度器来帮忙。顺便说一下sylar的协程调度器处理了协程抛出的异常并且给异常结束的协程设置了一个EXCEPT状态这看似贴心但从长远的角度来看其实是非常不利于协程的健康成长的。 8. 关于协程调度器的优雅停止。sylar停止调度器的策略如下 设置m_stopping标志该标志表示正在停止检测是否使用了caller线程进行调度如果使用了caller线程进行调度那要保证stop方法是由caller线程发起的通知其他调度线程的调度协程退出调度通知当前线程的调度协程退出调度如果使用了caller线程进行调度那执行一次caller线程的调度协程只使用caller线程时的协程调度全仰仗这个操作等caller线程的调度协程返回等所有调度线程结束 参考文献 上述内容与图像多来自网络和下列博客若侵权告知删除。 协程调度模块 - 类库与框架 - 程序员的自我修养 (midlane.top) 彻底理解协程 - 编码专家 - 博客园 (cnblogs.com)
http://www.w-s-a.com/news/593586/

相关文章:

  • 桂林网站制作培训学校外包seo公司
  • 莱州网站建设方案北京装修公司口碑
  • 大型网站建设济南兴田德润团队怎么样韩国女足出线了吗
  • 南通做网站找谁重庆网络推广网站推广
  • ps网站主页按钮怎么做怎样做网站的用户分析
  • 哪个网站做黑色星期五订酒店活动公司网络营销推广软件
  • 岳阳新网网站建设有限公司网页设计基础考试题目
  • 辽宁响应式网站费用海外平台有哪些
  • 杨凌规划建设局网站网站后台建设怎么进入
  • 有赞商城网站建设企业管理咨询是做什么的
  • 提供衡水网站建设中国石化工程建设有限公司邮政编码
  • 大芬地铁站附近做网站工业设计公司报价
  • 建设网站最强永年网站建设
  • 网站分站代理加盟wordpress国内工作室主题
  • 东营远见网站建设公司服装网站建设内容
  • 互助平台网站建设费用百度seo优化怎么做
  • lol英雄介绍网站模板工商局网上注册
  • 电商网站运营策划什么样的网站容易做seo
  • 网站备案需要什么流程怎么创建小程序卖东西
  • 陇西网站建设 室内设计持啊传媒企业推广
  • 连云港做网站制作首选公司如何让单位网站做防护
  • wordpress企业网站源码开发网站用什么工具做设计
  • 网站负责人不是法人seo神马网站推广器
  • 网站建设绩效考核方案wordpress支付宝付款
  • 高要区住房和城乡建设局网站如何网上注销自己的公司
  • 哪种技术做网站容易论文答辩图片做记录片的是哪个网站
  • 怎样在微信中做网站网站的备案号在哪
  • 返利淘网站怎么做wordpress htnl短代码
  • 网站 手机 appwordpress管理账户
  • 徐州网站建设 网站制作做招商网站的前景怎么样