纤程

纤程(fiber):

相当于用户级别的线程或轻进程.纤程由Win32库函数支持,对核心是不可见的.纤程可以通过SwitchToFiber显示至另一合作纤程,以实现合作纤程之间的协同.线程是在Windows内核中实现的,纤程是在用户模式下实现的,内核对纤程一无所知,内核会根据我们定义的算法来对纤程进行调度。一个线程可以包含一个或多个纤程。

与纤程有关的函数:

要使用纤程,首先要做的就是把当前线程转换为纤程:

PVOID ConvertThreadToFiber(PVOID pvParam);

  调用这个函数之后,系统为纤程执行环境分配大概200字节的存储空间,这个执行环境有以下内容构成:

1、用户定义的值,由参数pvParam参数指定。

2、结构化异常处理链头。

3、纤程内存栈的最高和最低地址,当线程转换为纤程的时候,这也是线程的内存栈。

4、各种CPU寄存器信息,比如堆栈指针寄存器,指令指针寄存器等等。

  默认情况下,x86系统的CPU的浮点数状态信息在纤程看来不属于CPU寄存器,因此会导致在纤程中执行一些相关的浮点运算会破坏数据。为了克服这个缺点,你需要呼叫ConvertThreadToFiberEx函数(Windows Vista及其以上版本中才有),并且传递FIBER_FLAG_FLOAT_SWITCH给它的第2个参数dwFlags:

PVOID ConvertThreadToFiberEx(    PVOID pvParam,    DWORD dwFlags);

  当呼叫完上述两个函数之后,你就初始化了一个纤程执行环境,该执行环境与线程的执行环境关联,线程转换为纤程,纤程就在线程的内部运行。ConvertThreadToFiber(Ex)函数实际返回纤程的执行环境的内存地址,你稍后会用到这个地址,但是你不能直接读取或写入这个地址,你应该使用系统提供的纤程函数来对这个地址进行操纵。

  当你的纤程返回或者呼叫ExitThread的时候,你的纤程也随之结束。

  如果一个线程中只有一个纤程,那么是没有必要将该线程转换为纤程的,只有你打算在同一个线程中再创建一个纤程才有转换的必要。要创建一个纤程,使用CreateFiber函数:

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

  这个函数创建一个新的堆栈,堆栈的大小由dwStackSize指定。如果传递0给它,就意味着创建一个默认大小的堆栈。

  如果你打算让一个线程包含多个纤程,而又想花费比较少的空间的话,可以使用CreateFiberEx函数(只有在Windows Vista及其以上版本中才有):

PVOID CreateFiberEx(    SIZE_T dwStackCommitSize,     // 堆栈初始提交的大小   SIZE_T dwStackReserveSize,    // 需要保留的虚拟内存的大小   DWORD dwFlags,     // 创建旗标   PFIBER_START_ROUTINE pStartAddress,     // 纤程函数指针   PVOID pvParam);     // 传递给纤程函数的参数

  其中,如果传递FIBER_FLAG_FLOAT_SWITCH给dwFlags参数,则表明将浮点信息添加到纤程执行环境。

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

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

VOID WINAPI FiberFunc(PVOID pvParam);

  这个纤程在第一次被调度的时候,纤程函数被调用,其参数pvParam由CreateFiber(Ex)中的pvParam参数指定。在纤程函数中,你可以做你想做的任何事情。

  像ConvertThreadToFiber(Ex)函数一样,CreateFiber(Ex)也返回纤程执行环境的内存地址,这个内存地址就像句柄一样,直接标识着一个纤程。

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

VOID SwitchToFiber(PVOID pvFiberExecutionContext);

  SwitchToFiber函数的参数是一个纤程执行环境的内存地址,该地址由ConverThreadToFiber(Ex)或CreateFiber(Ex)返回。

  SwitchToFiber函数内部的执行步骤如下:

1、保存当前的CPU寄存器信息,这些信息保存在正在运行的纤程的执行环境中。

2、从将要执行的纤程的执行环境中加载上次保存的CPU寄存器信息。

3、将即将执行的纤程执行环境与线程关联起来,由线程执行指定的纤程。

4、将指令指针设置为保存的值,继续上次的执行。

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

  最后,如果一个纤程完成了任务,你需要删除它,呼叫DeleteFiber函数,并传递这个纤程的执行环境内存地址:

VOID DeleteFiber(PVOID pvFiberExecutionContext);

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

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

  如果你需要在纤程中保存一些数据,可以使用“纤程局部存储”(FLS)的机制。这个机制和“线程局部存储”(TLS)类似。

  首先,呼叫FlsAlloc函数分配FLS槽来存放数据,这个FLS槽可以被当前进程内所有纤程共同使用,函数有一个参数:一个回调函数指针,这个回调函数会在以下两种情况下被调用:一个纤程被删除;FLS槽通过FlsFree函数被删除。

  然后,在你呼叫FlsAlloc函数之后,你可以在纤程中使用FlsSetValue函数来保存数据到FLS槽中,同时该函数需要一个DWORD类型的参数,表示一个FLS槽的索引,即在FLS槽的相关地方保存数据。

  接着,你可以在各个纤程中使用FlsGetValue函数来取得FLS槽中对应的数据,同样需要上面那个FLS槽索引,并返回指向数据的指针。

  当使用完这些数据之后,你可以使用FlsFree来释放FLS槽。

  如果你想知道你是否正在一个纤程执行环境中运行,可以使用IsThreadAFiber函数,它返回一个BOOL值,指明你是否正在一个纤程中运行。

  一个线程每次只能执行一个纤程,该纤程与这个线程相关联。你可以使用如下函数来得到正在执行的纤程的执行环境内存地址:

PVOID GetCurrentFiber();

  每个纤程包含用户定义的一个数据,这个数据由CreateFiber(Ex)或ConvertThreadToFiber(Ex)的pvParam参数指定,你可以使用如下函数得到这个数据的指针:

PVOID GetFiberData();

  最后,让我们假设一个线程中有2个纤程,总结一下纤程的用法:

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

2、定义一个纤程函数,用于创建一个新纤程

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

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

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

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

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

8、线程结束

Windows环境下多线程编程原理与应用读书笔记(3)————Windows环境中的多线程实现(3)的更多相关文章

  1. Windows环境下多线程编程原理与应用读书笔记(1)————基本概念

    自从学了操作系统知识后,我就对多线程比较感兴趣,总想让自己写一些有关多线程的程序代码,但一直以来,发现自己都没怎么好好的去全面学习这方面的知识,仅仅是完成了操作系统课程上的小程序,对多线程的理解也不是 ...

  2. Windows环境下多线程编程原理与应用读书笔记(8)————信号量及其应用

    <一>线程间同步原因 线程间竞争共享资源: 线程间为完成某个任务而协作: 通过互斥量可以实现线程间由于竞争所需要的同步,通过事件可以实现线程间由于协作所需要的同步. 信号量很好地将互斥量和 ...

  3. Windows环境下多线程编程原理与应用读书笔记(7)————事件及其应用

    <一>事件 事件主要用于线程间传递消息,通过事件来控制一个线程是处于执行状态还是处于挂起状态. 事件和互斥量之间的差别: 事件主要用于协调两个或者多个线程之间的动作,使其协调一致,符合逻辑 ...

  4. Windows环境下多线程编程原理与应用读书笔记(6)————临界段及其应用

    <一>临界段 临界段对象通过提供所有线程必须共享的对象来控制线程.只有拥有临界段对象的线程才能够访问保护的资源.在另一个线程可以访问该资源之前,前一线程必须释放临界段对象,一遍新的线程可以 ...

  5. Windows环境下多线程编程原理与应用读书笔记(5)————互斥及其应用

    <一>互斥的同步机制 思想:当一个线程获得互斥量了后,其他所有要获取同一个互斥量的线程都处于阻塞状态,直到第一个线程释放互斥量为止. 设想几个线程竞争同一个互斥量,其中一个线程获得了互斥量 ...

  6. Windows环境下多线程编程原理与应用读书笔记(2)————面向对象技术

    面向对象技术是学C++需要重点掌握的知识,因为我觉得自己的基础还是比较可以,这一章节的内容就只是粗略的读了一遍,在此就不做过多的笔记.

  7. Windows环境下多线程编程原理与应用读书笔记(4)————线程间通信概述

    <一>线程间通信方法 全局变量方式:进程中的线程共享全局变量,可以通过全局变量进行线程间通信. 参数传递法:主线程创建子线程并让子线程为其服务,因此主线程和其他线程可以通过参数传递进行通信 ...

  8. Java多线程编程实战指南 设计模式 读书笔记

    线程设计模式在按其有助于解决的多线程编程相关的问题可粗略分类如下. 不使用锁的情况下保证线程安全: Immutable Object(不可变对象)模式.Thread Specific Storage( ...

  9. iOS多线程编程原理及实践

    摘要:iOS开发中,开发者不仅要做好iOS的内存管理,而且如果你的iOS涉及多线程,那你也必须了解iOS编程中对多线程的限制,iOS主线程的堆栈大小为1M,其它线程均为512KB,且这个限制开发者是无 ...

随机推荐

  1. canvas浅谈 实现简单的自旋转下落

    旋转和平移是2个基础的动画效果,也是复杂动画的基础. 如果是普通的页面只要设置transform属性很容易实现平移+旋转的组合效果,达到自旋转下落的效果.因为操作的直接是动作元素本身很容易理解. 但是 ...

  2. python基础之七种运算符

    废话不多说,上节说的是数据类型,本篇讲讲数据运算. 在算式"1+2"中,"1"和"2"被称为操作数,"+"被称为运算符 ...

  3. Linux学习——shell编程之变量

    shell编程之变量:Linux shell编程基础中的变量. 包括Bash变量的分类和各变量的详细使用,如:用户自定义变量.环境变量.语系变量.位置参数变量和预定义变量. 1:什么是Bash变量? ...

  4. Nginx学习——Nginx启动、停止、重启和信号控制以及平滑升级

    1.Nginx 启动与停止 (1)启动方式 启动格式:Nginx可执行文件地址 -c Nginx配置文件地址 /etc/local/nginx/sbin/nginx -c /root/dufy/ngi ...

  5. Qt中的坐标系统

    Qt使用统一的坐标系统来定位窗口部件的位置和大小. 以屏幕的左上角为原点即(0, 0)点,从左向右为x轴正向,从上向下为y轴正向,这整个屏幕的坐标系统就用来定位顶层窗口: 此外,窗口内部也有自己的坐标 ...

  6. 分享基于分布式Http长连接框架--架构模型

    我画了个简单的架构图来帮助说明: 其实为发布订阅架构模式. 生产者和消费者我们统一可理解为客户端,消息中间件可认为是服务端. 生产者和消费者做为客户端要跟服务端交互,则先通过代理订阅服务端,订阅成功后 ...

  7. Twitter的分布式系统中ID生成方法——Snowflake

    Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统 ...

  8. Python 实现的随机森林

    随机森林是一个高度灵活的机器学习方法,拥有广泛的应用前景,从市场营销到医疗保健保险. 既可以用来做市场营销模拟的建模,统计客户来源,保留和流失.也可用来预测疾病的风险和病患者的易感性. 随机森林是一个 ...

  9. java数据库编程之嵌套子查询及exists的使用

    第四章:高级查询(二) 4.1:exists和not exists子查询 4.1.1:exists子查询 用exists作为子查询的where条件 语法:select,,,,,,from 表名   w ...

  10. SQL监测语句

    SELECT top 20 qs.creation_time,last_execution_time,total_physical_reads,total_logical_reads,total_lo ...