纤程

1、定义

       纤程是 fiber , 是由操作系统实现的一种轻量化线程上的一个执行结构。 通常是多个fiber共享一个固定的线程, 然后他们通过互相主动切换到其他fiber来交出线程的执行权.。各个子任务之间的关系非常强。

       在Windows2000/XP中,纤程(fiber)相当于用户级别的线程或轻进程.纤程由Win32库函数支持,对核心是不可见的.纤程可以通过SwitchToFiber切换至另一合作纤程,以实现合作纤程之间的协同。

 

2、优缺点

(1)优点

  • 消耗小,切换快,一个进程可以创建成千上万个纤程。
  • 小任务顺序编程很符合人的思维方式, 规避纯异步编程中状态机的复杂性. 使得使用纤程写的程序将更加的直观, 逻辑描述方便, 简化编程.纤程用于化异步为同步, 你可以进行一个异步操作以后就切换纤程,等到异步操作完成以后在切换回来,这样,在逻辑上相关的代码就可以写到一个函数里面,而不用人为的分到多个回调函数中。
  • 没有了线程所谓的安全问题, 避免锁机制。

(2)缺点

  • 纤程一般只支持所有的纤程函数在一个线程里面跑. 无法充分利用多核CPU, 除非把所有的IO和计算操作都剥离成单独的线程。
  • 关于跨平台的纤程的实现和使用资料较少。

 

3、CreateFiber

PVOID CreateFiber(DWORD dwStackSize, PFIBER_START_ROUTINE pfnStartAddress, PVOID pvParam);
  • DWORD dwStackSize,  // 创建新的堆栈的大小,0表示默认大小
  • PFIBER_START_ROUTINE pfnStartAddress,   // 纤程函数地址    
  • PVOID pvParam              // 传递给纤程函数的参数

       当CreateFiber(Ex)函数创建了一个新的堆栈之后,它分配一个新的纤程执行环境结构并初始化之,用户定义的数据通过pvParam参数被保存,新的堆栈的内存空间的最高和最低地址被保存,纤程函数的地址通过pStartAddress参数被保存。

纤程函数的格式必须如下定义:

VOID WINAPI FiberFunc(PVOID pvParam);

       这个纤程在第一次被调度的时候,纤程函数被调用,其参数pvParam由CreateFiber(Ex)中的pvParam参数指定。在纤程函数中,你可以做你想做的任何事情。像ConvertThreadToFiber(Ex)函数一样,CreateFiber(Ex)也返回纤程执行环境的内存地址,这个内存地址就像句柄一样,直接标识着一个纤程。

       当你使用CreateFiber(Ex)函数创建一个纤程之后,该纤程不会执行,因为系统不会自动调度它。你必须调用函数SwitchToFiber来告诉系统你想要哪个纤程执行。SwitchToFiber函数内部的执行步骤如下:

  • 保存当前的CPU寄存器信息,这些信息保存在正在运行的纤程的执行环境中。
  • 从将要执行的纤程的执行环境中加载上次保存的CPU寄存器信息。
  • 将即将执行的纤程执行环境与线程关联起来,由线程执行指定的纤程。
  • 将指令指针设置为保存的值,继续上次的执行。

       SwitchToFiber函数是一个纤程能够被调度的唯一的方法,因此,纤程的调度是由用户完全操纵的。纤程的调度和线程的调度无关。一个线程,包含了正在运行的纤程,仍会被其他线程抢占。当一个线程被调度,而它里面有几个纤程,那么只有被选择的那个纤程才会执行,其他纤程的执行需要调用SwitchToFiber函数。

 

4、DeleteFiber函数

       由一个纤程调用来删除另一个纤程。

扫描二维码关注公众号,回复: 12880683 查看本文章

       该函数首先清除纤程堆栈,然后删除纤程执行环境。但是,如果参数指定的是一个与当前线程关联的纤程,该函数呼叫ExitThread函数,线程结束,其包含的其他纤程也都结束。因此,DeleteFiber函数一般是由一个纤程调用来删除另一个纤程。

  当所有纤程结束了运行,你需要从纤程转换为线程,呼叫ConvertFiberToThread函数。

 

5、一个线程每次只能执行一个纤程,该纤程与这个线程相关联。

(1)你可以使用如下函数来得到正在执行的纤程的执行环境内存地址,返回当前纤程的指针:

         PVOID GetCurrentFiber();其实这是个宏

(2)通过下函数获取当前纤程数据的指针:

         GetFiberData();也是个宏

 

6、小结

       假设一个线程中有2个纤程,总结一下纤程的用法:

(1)使用ConverThreadToFiber(Ex)将当前线程转换到纤程,这是纤程F1

(2)定义一个纤程函数,用于创建一个新纤程:VOID CALLBACK FiberProc();

(3)纤程F1中调用CreateFiber(Ex)函数创建一个新的纤程F2

(4)SwitchToFiber函数进行纤程切换,让新创建的纤程F2执行

(5)F2纤程函数执行完毕的时候,使用SwitchToFiber转换到F1

(6)在纤程F1中调用DeleteFiber来删除纤程F2

(7)纤程F1中调用ConverFiberToThread,转换为线程

(8)线程结束。

 

7、Tars协程。

     tars中的协程在用于任务调度时与以上介绍的协程使用上有所区别。在官方的举例中:

    1)继承自Coroutine

    2)重载实现handle

    3)调用 setCorolInfo 初始化相关信息

/**
 * 初始化
 * @iNum, 表示要运行多个协程,即会有这个类多少个coroRun运行
 * @iTotalNum,表示这个线程最多包含的协程个数
 * @iStackSize,协程的栈大小
 */
 void setCoroInfo(uint32_t iNum, uint32_t iMaxNum, size_t iStackSize);  

    4)Start 启动

           通过查看源码可以看到Coroutine继承自线程,handle相当于一般线程类中run函数。

           因此本质上是另外开启了1个线程,在开启的线程中启动了iNum个数的协程,协程执行的函数为handle函数(相互不影响)。

 

猜你喜欢

转载自blog.csdn.net/King_weng/article/details/113393405