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] = '\0'; 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]。\n"), GetLastError()); return RTN_ERROR; } g_lpBuffer = (LPBYTE)QM_CALLOC(BUFFER_SIZE); if (g_lpBuffer == NULL){ _tprintf(_T("HeapAlloc失败[%d]。\n"), 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]。\n"), 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]。\n"), GetLastError()); return RTN_ERROR; } //主线程变为主纤程 g_lpFiber[PRIMARY_FIBER] = ConvertThreadToFiber(&fs[PRIMARY_FIBER]); if (g_lpFiber[PRIMARY_FIBER] == NULL){ _tprintf(_T("ConvertThreadToFiber出错(%d)\n"), 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)\n"), 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)\n"), GetLastError()); return RTN_ERROR; } //开始执行读纤程 SwitchToFiber(g_lpFiber[READ_FIBER]); _tprintf(_T("读纤程:结果代码为%lu,%lu字节被处理\n"), fs[READ_FIBER].dwFiberResultCode, fs[READ_FIBER].dwBytesProcessed); _tprintf(_T("写纤程:结果代码为%lu,%lu字节被处理\n"), 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,退出当前线程\n")); 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,退出当前线程.\n")); 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\n"), 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)\n"), 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)\n"), 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)\n"), 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\n"), pFiber, (pszFlsValue == NULL)? TEXT("'Null 值'"):(PCTSTR)pszFlsValue, (pFlsValue == NULL) ? TEXT("'Null 值'") : (PCTSTR)pFlsValue); } else{ StringCchCopy(szMsg, _countof(szMsg), TEXT("不再是一个纤程...\n")); } 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\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" 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