第12章 纤程(Fiber)

12.1 纤程对象的介绍

(1)纤程与线程的比较

比较

线程(Thread)

纤程(Fiber)

实现方式

是个内核对象

在用户模式中实现的一种轻量级的线程,是比线程更小的调度单位。

调度方式

由Microsoft定义的算法来调度,操作系统对线程了如指掌。内核对线程的调度是抢占式的。

由我们自己调用SwitchToFiber来调度,内核对纤程一无所知。线程一次只能执行一个纤程代码,纤程间的调度不是抢占式的。

备注

一个线程可以包含一个或多个纤程。操作系统随时可能夺走纤程所在线程的运行。当线程被调度时,当前被选择的纤程得以运行,而其他纤程无法运行,因为同一个线程中,每次只能有一个纤程正在运行,除非调用SwitchToFiber才能切换到另一个纤程去执行。与SwitchToThread不同,SwitchToFiber会立即切换到另一个纤程去执行(如果该线程的CPU时间还有剩余的话),而SwitchToThread要等CPU来调度另一个线程。

纤程与线程一样,也有自己的寄存器环境与函数调用栈

(2)纤程的执行上下文的构成(类似线程上下文)——大约200个字节

  ①用户自定义的值,它被初始化为传给ConvertThreadToFiber的pvParam参数的值

  ②结构化异常处理链的头

  ③纤程栈顶部和底部的内存地址(当我们将一个线程转换为一个纤程时,这时也是线程栈)

  ④某些CPU寄存器,其中包括栈指针、指令指针以及其他寄存器(注意,默认下不包含CPU的浮点状态信息)

(3)纤程运行动态示意图

 

★注意:

  在同一个线程里创建的两个纤程之间的切换是很安全的(如图中A箭头),但跨线程间的两个纤程的切换是不安全的(如图中的B、C箭头)。因为纤程本质上是由线程调度的,假设某个时刻,线程2正在调用纤程2.2,但在纤程2.2的内部调用了SwitchToFiber切换到了纤程1.2。如果CPU的下一个时间周期仍给线程2,因为内核并不知道纤程的切换,所以此时CPU仍会试图去执行纤程2.2的代码,但由于纤程的切换,会导致线程2的堆栈环境发生了变化,此时再去执行纤程2.2就可能会出现错误。

12.2 纤程的使用

(1)创建主纤程:CreateThreadToFiber(pvParam)(将已有线程转为纤程,该线程才能调用其它纤程API函数,可理解为启动线程的纤程模式

  ★注意:

    ①返回值为纤程的上下文环境,可以理解为返回一个纤程对象。

    ②默认情况下,x86 CPU的FPU信息不会被纤悉无纤程保存下来,因此在进行浮点运算时,可能破坏数据。为避免此情况,要调该新的ConvertThreadToFiberEx函数,并为dwFlags传入FIBER_FLAG_FLOAT_SWITCH标志。

(2)创建纤程(可理解为子纤程):CreateFiber

参数

描述

DWORD dwStackSize

纤程栈大小。一般传入0,表示系统自动分配

PFIBER_START_ROUTINE

  pfnStartAddress

纤程函数,原型为

VOID WINAPI FiberFunc(PVOID pvParam)

PVOID pvParam

传给纤程函数的额外参数。

  ★注意:

    ①返回值为纤程的上下文环境,可以理解为返回一个纤程对象。

    ②同样,为防止发生浮点运算事故,可以调用新的API函数CreateFiberEx,并传入FIBER_FLAG_FLOAT_SWITCH标志。

(3)纤程的调度SwitchToFiber(PVOID pvFiberExcutionContext)函数,其中的参数是CreateFiber或CreateThreadToFiber返回的纤程对象(即纤程上下文环境)。注意:SwitchToFiber是让纤程得到CPU时间的唯一方法!由于我们必须显示调用SwitchtoFiber来让纤程有机会得到执行,因此纤程的调度完全在我们的掌握之中。

  ①SwitchToFiber函数的内部运行

  A.将一些CPU寄存器当前值(包括指令指针寄存器和栈指针寄存器),保存到当前正在运行的纤程的执行上下文中。

  B.从即将运行的纤程的执行上下文中,将先前保存的寄存器载入CPU寄存器。使用当线程继续执行的时候,会使用新纤程的运行环境(如栈、指令指针)

  C.将新纤程上下文与线程关联起来,让线程运行指定的纤程。

  D.将线程的指令指针设为新纤程先前保存的指令指针,这样线程(纤程)就会从上次执行的地方开始继续往下执行。

(4)纤程的删除:DeleteFiber(PVOID pvFiberExecutionContext);

  ①当纤程执行结束后,调用该函数来销毁纤程,被删除的纤程的栈将被销毁,纤程执行的上下文也会被释放。

  ②如果纤程是ConvertThreadToFiber转换得到的主纤程,当调用DeleteFiber相当于调用ExitThread直接终止线程。如果不希望终止线程,可以调用ConvertFiberToThread将主纤程转回线程,这里也会释放原来调用ConverThreadToFiber将线程转化为纤程时所占用的最后一块内存。注意,ConvertFiberToThread只转换主纤程,对其它子纤程无效

【Fiber程序】

 

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <locale.h>

//////////////////////////////////////////////////////////////////////////
#define QM_ALLOC(sz)    HeapAlloc(GetProcessHeap(),0,sz)
#define QM_CALLOC(sz)   HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz)
#define QM_SAFEFREE(p)  if(NULL !=p){HeapFree(GetProcessHeap(),0,p);p=NULL;}

//////////////////////////////////////////////////////////////////////////
#define BUFFER_SIZE  32768 //32*1024,即32K
#define FIBER_COUNT  3  //最大的纤程数(包含主纤程)
#define PRIMARY_FIBER 0 //主纤程的索引
#define READ_FIBER    1 //读纤程的索引
#define WRITE_FIBER   2 //写纤程的索引

#define RTN_OK    0  //RTN =Return
#define RTN_USAGE 1 
#define RTN_ERROR  13

//////////////////////////////////////////////////////////////////////////
LPVOID  g_lpFiber[FIBER_COUNT];
LPBYTE  g_lpBuffer;
DWORD   g_dwBytesRead; //分批读取的字节数,要在读和写纤程中共享这个变量

//////////////////////////////////////////////////////////////////////////
typedef struct{
    DWORD dwParamter;    //DWORD parameter to Fiber(unnsed)
    DWORD dwFiberResultCode;  //GetLastError result code
    HANDLE hFile;             //handle to operate on
    DWORD  dwBytesProcessed;    //number of bytes to processed
}FIBERDATASTRUCT,*PFIBERDATASTRUCT,*LPFIBERDATASTRUCT;

VOID DisplayFiberInfo(void);
VOID WINAPI ReadFiberFunc(LPVOID lpParameter);
VOID WINAPI WriteFiberFunc(LPVOID lpParameter);

//////////////////////////////////////////////////////////////////////////
__inline VOID GetAppPath(LPTSTR pszBuffer){
    DWORD dwLen = 0;
    if (0 == (dwLen = GetModuleFileName(NULL, pszBuffer, MAX_PATH))){
        return;
    }

    DWORD i = dwLen;
    for (; i > 0;i--){
        if ('\'==pszBuffer[i]){
            pszBuffer[i + 1] = '';
            break;
        }
    }
}

//////////////////////////////////////////////////////////////////////////
int _tmain(){
    _tsetlocale(LC_ALL, _T("chs"));

    LPFIBERDATASTRUCT fs = NULL;

    TCHAR pSrcFile[MAX_PATH] = {};
    TCHAR pDstFile[MAX_PATH] = {};

    GetAppPath(pSrcFile);
    GetAppPath(pDstFile);
    StringCchCat(pSrcFile, MAX_PATH, TEXT("2.jpg"));
    StringCchCat(pDstFile, MAX_PATH, TEXT("2_Cpy.jpg"));

    fs = (LPFIBERDATASTRUCT)QM_CALLOC(sizeof(FIBERDATASTRUCT)*FIBER_COUNT);
    if (fs == NULL){
        _tprintf(_T("HeapAlloc失败[%d]。
"), GetLastError());
        return RTN_ERROR;
    }

    g_lpBuffer = (LPBYTE)QM_CALLOC(BUFFER_SIZE);
    if (g_lpBuffer == NULL){
        _tprintf(_T("HeapAlloc失败[%d]。
"), GetLastError());
        return RTN_ERROR;
    }

    fs[READ_FIBER].hFile = CreateFile(pSrcFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
                                      FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if (fs[READ_FIBER].hFile ==INVALID_HANDLE_VALUE){
        _tprintf(_T("CreateFile失败[%d]。
"), GetLastError());
        return RTN_ERROR;
    }

    fs[WRITE_FIBER].hFile = CreateFile(pDstFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                                      FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (fs[WRITE_FIBER].hFile == INVALID_HANDLE_VALUE){
        _tprintf(_T("CreateFile失败[%d]。
"), GetLastError());
        return RTN_ERROR;
    }

    //主线程变为主纤程
    g_lpFiber[PRIMARY_FIBER] = ConvertThreadToFiber(&fs[PRIMARY_FIBER]);
    if (g_lpFiber[PRIMARY_FIBER] == NULL){
        _tprintf(_T("ConvertThreadToFiber出错(%d)
"), GetLastError());
        return RTN_ERROR;
    }

    fs[PRIMARY_FIBER].dwParamter = 0;
    fs[PRIMARY_FIBER].dwFiberResultCode = 0;
    fs[PRIMARY_FIBER].hFile = INVALID_HANDLE_VALUE;

    //创建读纤程
    fs[READ_FIBER].dwParamter = 0x12345678;
    g_lpFiber[READ_FIBER] = CreateFiber(0, ReadFiberFunc, &fs[READ_FIBER]);
    if (g_lpFiber[READ_FIBER] == NULL){
        _tprintf(_T("CreateFiber出错(%d)
"), GetLastError());
        return RTN_ERROR;
    }

    //创建写纤程
    fs[WRITE_FIBER].dwParamter = 0x54545454;
    g_lpFiber[WRITE_FIBER] = CreateFiber(0, WriteFiberFunc, &fs[WRITE_FIBER]);
    if (g_lpFiber[WRITE_FIBER] == NULL){
        _tprintf(_T("CreateFiber出错(%d)
"), GetLastError());
        return RTN_ERROR;
    }
    
    //开始执行读纤程
    SwitchToFiber(g_lpFiber[READ_FIBER]);

    _tprintf(_T("读纤程:结果代码为%lu,%lu字节被处理
"),
             fs[READ_FIBER].dwFiberResultCode, fs[READ_FIBER].dwBytesProcessed);

    _tprintf(_T("写纤程:结果代码为%lu,%lu字节被处理
"),
             fs[WRITE_FIBER].dwFiberResultCode, fs[WRITE_FIBER].dwBytesProcessed);

    DeleteFiber(g_lpFiber[READ_FIBER]);
    DeleteFiber(g_lpFiber[WRITE_FIBER]);

    CloseHandle(fs[READ_FIBER].hFile);
    CloseHandle(fs[WRITE_FIBER].hFile);

    QM_SAFEFREE(g_lpBuffer);
    QM_SAFEFREE(fs);

    //纤程变回线程
    ConvertFiberToThread();

    _tsystem(_T("PAUSE"));
    return RTN_OK;
}

VOID WINAPI ReadFiberFunc(LPVOID lpParameter){
    LPFIBERDATASTRUCT pfds = (LPFIBERDATASTRUCT)lpParameter;

    if (pfds == NULL){
        _tprintf(_T("传递的纤程数据为NULL,退出当前线程
"));
        return;
    }

    pfds->dwBytesProcessed = 0;
    while (1){
        DisplayFiberInfo();
        if (!ReadFile(pfds->hFile,g_lpBuffer,BUFFER_SIZE,&g_dwBytesRead,NULL)){
            break;
        }
        if (g_dwBytesRead ==0){
            break;
        }

        pfds->dwBytesProcessed += g_dwBytesRead;
        SwitchToFiber(g_lpFiber[WRITE_FIBER]);
    }  //while

    pfds->dwFiberResultCode = GetLastError();
    SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}


VOID WINAPI WriteFiberFunc(LPVOID lpParameter){
    LPFIBERDATASTRUCT pfds = (LPFIBERDATASTRUCT)lpParameter;

    DWORD dwBytesWritten;
    if (pfds == NULL){
        _tprintf(_T("传递的纤程数据为NULL,退出当前线程.
"));
        return;
    }

    pfds->dwBytesProcessed = 0;
    pfds->dwFiberResultCode = ERROR_SUCCESS;

    while (1){
        DisplayFiberInfo();
        if (!WriteFile(pfds->hFile,g_lpBuffer,g_dwBytesRead,&dwBytesWritten,NULL)){
            break;
        }

        pfds->dwBytesProcessed += dwBytesWritten;
        SwitchToFiber(g_lpFiber[READ_FIBER]); //接着读取数据
    }//while

    pfds->dwFiberResultCode = GetLastError();
    SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}

VOID DisplayFiberInfo(void){
    LPFIBERDATASTRUCT pfds = (LPFIBERDATASTRUCT)GetFiberData();
    LPVOID  lpCurrentFiber = GetCurrentFiber();

    if (lpCurrentFiber == g_lpFiber[READ_FIBER]){
        _tprintf(_T("读纤程进入!"));
    } else{
        if (lpCurrentFiber == g_lpFiber[WRITE_FIBER]){
            _tprintf(_T("写纤程进入!"));        
        } else{
            if (lpCurrentFiber == g_lpFiber[PRIMARY_FIBER]){
                _tprintf(_T("主纤程进入!"));
            } else{
                _tprintf(_T("未知纤程进入!"));
            }        
        }    
    }

    _tprintf(_T("dwParameter为0x%1X
"), pfds->dwParamter);
}

12.3 纤程的本地存储(Fiber Local Storage,FLS)

(1)使用FLS的步骤(类似于TLS):

  ①调用FlsAlloc分配FLS索引

  ②调用FlsSetValue将Fiber的值写入该索引FLS

  ③调用FlsGetValue取得Fiber存储的FLS值

  ④调用FlsFree释放FLS索引

(2)FlsAlloc中可以指定一个回调函数:VOID WINAPI FlsCallback(PVOID lpFlsData);

  【说明】回调函数会在纤程被销毁、调度纤程的线程退出或FlsFree时被调用,这主要便纤程有机会删除自己在FLS中存储的值。

(3)获取当前纤程对象(上下文环境):PVOID GetCurrentFiber();

(4)获得创建纤程时的pvParam数据:GetFiberData

(5)判断是否正在某个纤程中运行:IsThreadFiber()

【FLS程序】演示如何使用纤程本地存储

#include <tchar.h>
#include <windows.h>
#include <strsafe.h>
#include <locale.h>

//////////////////////////////////////////////////////////////////////////
#define QM_ALLOC(sz)    HeapAlloc(GetProcessHeap(),0,sz)
#define QM_CALLOC(sz)   HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz)
#define QM_SAFEFREE(p)  if (NULL != p){HeapFree(GetProcessHeap(),0,p);p=NULL;}

//////////////////////////////////////////////////////////////////////////
#define  FIBER_COUNT 3
#define  PRIMARY_FIBER  0  //主纤程的数组索引

LPVOID  g_lpFiber[FIBER_COUNT] = {};
DWORD  g_dwFlsIndex = 0;

//////////////////////////////////////////////////////////////////////////
VOID _stdcall FiberFunc(LPVOID lpParameter);

int _tmain(){
    _tsetlocale(LC_ALL, _T("chs"));

    //主线程变为纤程
    g_lpFiber[PRIMARY_FIBER] = ConvertThreadToFiber(NULL);
    if (g_lpFiber[PRIMARY_FIBER]==NULL){
        _tprintf(_T("ConvertThreadToFiber出错(%d)
"), GetLastError());
        return -1;
    }

    g_dwFlsIndex = FlsAlloc(NULL);
    if (FLS_OUT_OF_INDEXES == g_dwFlsIndex){
        _tprintf(_T("FlsAlloc出错(%d)"), GetLastError());
        return -1;
    }

    //创建2个子纤程
    for (int i = 1; i< FIBER_COUNT;i++){
        g_lpFiber[i] = CreateFiber(0, FiberFunc, NULL);
        if (g_lpFiber[i]==NULL){
            _tprintf(_T("CreateFiber出错(%d)
"), GetLastError());
            return -1;
        }
    }

    //轮流调度
    for (int i = 1; i < FIBER_COUNT;i++){
        SwitchToFiber(g_lpFiber[i]);
    }

    //删除纤程
    for (int i = 1; i < FIBER_COUNT;i++){
        DeleteFiber(g_lpFiber[i]);
    }

    FlsFree(g_dwFlsIndex);

    //纤程变回线程
    ConvertFiberToThread();

    _tsystem(_T("PAUSE"));
    return 0;
}

VOID _stdcall FiberFunc(LPVOID lpParameter){
    FlsSetValue(g_dwFlsIndex, GetCurrentFiber());
    _tprintf(_T("纤程[0x%x]保存的Fls值(%u)
"), 
             GetCurrentFiber(),FlsGetValue(g_dwFlsIndex));
    SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}

【Counter示例程序】使用纤程进行计数

      

             计算中                                     窗口拖动中(重算纤程停止)

/*************************************************************************
Module: Counter.cpp
Notices:Copyright(c) 2008 Jeffrey Ritchter & Christophe Nasarre
*************************************************************************/
#include "../../CommonFiles/CmnHdr.h"
#include <tchar.h>
#include <strsafe.h>
#include "resource.h"

//////////////////////////////////////////////////////////////////////////
//后台处理过程(Background Processing)可能的状态
typedef enum{
    BPS_STARTOVER,//从新开始后台处理过程
    BPS_CONTINUE, //继续后台处理过程
    BPS_DONE      //后台处理结束
}BKGNDPROCSTATE;

typedef struct{
    PVOID pFiberUI; //用户纤程对象(上下文)
    HWND  hwnd;     //UI窗口句柄
    BKGNDPROCSTATE bps; //后台处理过程的状态
}FIBERINFO,*PFIBERINFO;

//////////////////////////////////////////////////////////////////////////
//应用程序运行状态,该变量可以由UI纤程直接访问,后台处理过程可间接地访问
FIBERINFO g_FiberInfo;

//纤程局部存储索引值(FLS槽)
DWORD  g_dwSlot = 0;

//////////////////////////////////////////////////////////////////////////
//FlsAlloc中指定的回调函数
VOID WINAPI LogMessage(PVOID pFlsValue){
    TCHAR szMsg[MAX_PATH];

    //检查线程是否在纤程中,因为该回调函数可能在纤程之外被调用
    //只有在纤程中运行,才能使用FLS槽里的值
    if (IsThreadAFiber()){
        PVOID pFiber= GetCurrentFiber();
        PCTSTR pszFlsValue = (PCTSTR)FlsGetValue(g_dwSlot);
        StringCchPrintf(szMsg, _countof(szMsg), TEXT("[0x%x - %s] %s
"), 
                        pFiber,
                        (pszFlsValue == NULL)? TEXT("'Null 值'"):(PCTSTR)pszFlsValue,
                        (pFlsValue == NULL) ? TEXT("'Null 值'") : (PCTSTR)pFlsValue);
    } else{
        StringCchCopy(szMsg, _countof(szMsg), TEXT("不再是一个纤程...
"));
    }

    OutputDebugString(szMsg);
}

//////////////////////////////////////////////////////////////////////////
void WINAPI FiberFunc(LPVOID lpParameter){
    PFIBERINFO pFiberInfo = (PFIBERINFO)lpParameter;

    FlsSetValue(g_dwSlot, TEXT("Computation"));
    LogMessage(TEXT("进入 计算中..."));

    //显示当前正在运行的纤程
    SetDlgItemText(pFiberInfo->hwnd, IDC_FIBER, TEXT("后台重算纤程"));

    //获取当前编辑框中的计数器值
    int nCount = GetDlgItemInt(pFiberInfo->hwnd, IDC_COUNT, NULL, FALSE);

    //从0到计数到nCount,并更新Answer的内容
    for (int x = 0; x <= nCount;x++){
        //检查线程的消息队列是否有新的消息(在同一个线程中运行的所有
        //纤程共享该线程的消息队列)
        if (HIWORD(GetQueueStatus(QS_ALLEVENTS))!=0){
            //UI纤程事件到达,先去暂停后台处理 ,转去处理UI事件
            SwitchToFiber(pFiberInfo->pFiberUI);

            //UI事件处理完毕,后台继续计算
            SetDlgItemText(pFiberInfo->hwnd, IDC_FIBER, TEXT("后台重算纤程"));
        }

        //更新Answer
        SetDlgItemInt(pFiberInfo->hwnd, IDC_ANSWER, x, FALSE);

        //为了夸大效果,睡眠一会儿
        Sleep(200);
    }

    //计算结束
    pFiberInfo->bps = BPS_DONE;

    //重新调度UI线程。当线程正在运行并且没有UI事件可处理时,线程将进入睡眠状态
    //(因为UI线程调用了WaitMessage)
    SwitchToFiber(pFiberInfo->pFiberUI);
}

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){
    SetDlgItemInt(hwnd, IDC_COUNT, 0, FALSE);
    return TRUE;
}

void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotify){
    switch (id)
    {
    case IDCANCEL:
        PostQuitMessage(0);
        break;

    case IDC_COUNT:
        if (codeNotify == EN_CHANGE){
            //当用户改变了计数值 ,重新开始后台处理过程
            g_FiberInfo.bps = BPS_STARTOVER;    
        }
        break;
    }
}
//////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
    switch (uMsg)
    {
        chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
        chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
    }
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nShowCmd)
{
    //后台处理的纤程对象
    PVOID  pFiberCounter = NULL;

    //主线程转为主纤程
    g_FiberInfo.pFiberUI = ConvertThreadToFiber(NULL);
    
    g_dwSlot = FlsAlloc(LogMessage); //Fls索引
    FlsSetValue(g_dwSlot, TEXT("UI Fiber"));

    //创建应用程序UI窗口
    g_FiberInfo.hwnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_COUNTER), NULL, Dlg_Proc);

    //更新显示当前正在运行的纤程
    SetDlgItemText(g_FiberInfo.hwnd, IDC_FIBER, TEXT("用户界面纤程"));

    //初始化时,当前没有后台处理任务
    g_FiberInfo.bps = BPS_DONE;

    //消息循环
    BOOL fQuit = FALSE;
    while (!fQuit){
        //UI消息比后台处理过程有更高的优先级
        MSG msg;
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
            if (!IsDialogMessage(g_FiberInfo.hwnd,&msg)){
                TranslateMessage(&msg);
                DispatchMessage(&msg);    
            }
            fQuit = (msg.message == WM_QUIT);
            
            if (fQuit){
                //释放FLS槽
                FlsFree(g_dwSlot);

                //停止后台处理过程
                if (pFiberCounter !=NULL){
                    DeleteFiber(pFiberCounter);
                    pFiberCounter = NULL;
                }
                //退出纤程模式并返回单线程模式
                ConvertFiberToThread();
                g_FiberInfo.pFiberUI = NULL;
            }
        } else{ //没有UI消息时,检查后台过程的状态
            switch (g_FiberInfo.bps)
            {
            case BPS_DONE:
                //没有后台过程则等待UI事件
                WaitMessage();
                break;

            case BPS_STARTOVER:
                //用户改变了计数值
                //先取消当前的后台处理程序,然后重新开始后台处理
                if (pFiberCounter !=NULL){
                    DeleteFiber(pFiberCounter);
                    pFiberCounter = NULL;                
                }
                //将主线程转化为主纤程
                if (g_FiberInfo.pFiberUI ==NULL){
                    g_FiberInfo.pFiberUI = ConvertThreadToFiber(NULL);
                }
                
                //LogMessage
                LogMessage(TEXT("转换UI线程为纤程中..."));

                //创建一个新的重计算纤程
                pFiberCounter = CreateFiber(0, FiberFunc, &g_FiberInfo);

                //后台处理进程开始
                g_FiberInfo.bps = BPS_CONTINUE; 
                                         //注意,这里没有break,贯穿执行下去。
            case BPS_CONTINUE:
                //允许后台处理开始
                SwitchToFiber(pFiberCounter);

                //后台处理被暂停(可能因为UI消息或被计算完成被自动暂停)
                //显示哪个纤程正在被执行中
                SetDlgItemText(g_FiberInfo.hwnd, IDC_FIBER, TEXT("用户界面纤程"));

                if (g_FiberInfo.bps == BPS_DONE){
                    //完成后台处理,删除纤程以便下次重新执行计算
                    DeleteFiber(pFiberCounter);
                    pFiberCounter = NULL;

                    //退出纤程模式并重回单线程模式
                    ConvertFiberToThread();
                    g_FiberInfo.pFiberUI = NULL;
                }
                break;
            } //Switch,后台处理状态

        } //没有UI消息
    } //While,窗口仍然存在

    DestroyWindow(g_FiberInfo.hwnd);
    return 0; //结束程序
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 12_Counter.rc 使用
//
#define IDD_COUNTER                     101
#define IDC_COUNT                       1001
#define IDC_ANSWER                      1002
#define IDC_FIBER                       1003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1004
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Counter.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_COUNTER DIALOGEX 0, 0, 160, 54
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "计数器"
FONT 10, "宋体", 400, 0, 0x86
BEGIN
    LTEXT           "计数到:",IDC_STATIC,15,15,33,8
    EDITTEXT        IDC_COUNT,50,12,40,14,ES_AUTOHSCROLL | ES_NUMBER
    LTEXT           "当前值:",IDC_STATIC,96,15,31,8
    LTEXT           "0",IDC_ANSWER,127,15,19,8
    LTEXT           "当前正在运行的纤程:",IDC_STATIC,14,36,74,8
    LTEXT           "Fiber",IDC_FIBER,94,36,55,8
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_COUNTER, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 153
        TOPMARGIN, 7
        BOTTOMMARGIN, 47
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
原文地址:https://www.cnblogs.com/5iedu/p/4830983.html