zoukankan      html  css  js  c++  java
  • 第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
  • 相关阅读:
    颠覆想象的php解析获取跨域HTML标签
    Win7承载网络配置——让你的手机无线上网吧
    Joomla!备忘手记
    jQuery+PHP+MySQL简单无限级联实现
    js 功能函数集
    PHP POST数据至远程服务器获取信息
    js生成迅雷地址
    强大的jQuery选择器之选定连续多行
    札记 PHP/JS/jQuery/MySQL/CSS/正则/Apache
    php数组一对一替换
  • 原文地址:https://www.cnblogs.com/5iedu/p/4830983.html
Copyright © 2011-2022 走看看