第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 = ;
if ( == (dwLen = GetModuleFileName(NULL, pszBuffer, MAX_PATH))){
return;
} DWORD i = dwLen;
for (; i > ;i--){
if ('\\'==pszBuffer[i]){
pszBuffer[i + ] = '\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, , 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 = ;
fs[PRIMARY_FIBER].dwFiberResultCode = ;
fs[PRIMARY_FIBER].hFile = INVALID_HANDLE_VALUE; //创建读纤程
fs[READ_FIBER].dwParamter = 0x12345678;
g_lpFiber[READ_FIBER] = CreateFiber(, 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(, 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 = ;
while (){
DisplayFiberInfo();
if (!ReadFile(pfds->hFile,g_lpBuffer,BUFFER_SIZE,&g_dwBytesRead,NULL)){
break;
}
if (g_dwBytesRead ==){
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 = ;
pfds->dwFiberResultCode = ERROR_SUCCESS; while (){
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 = ; //////////////////////////////////////////////////////////////////////////
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 -;
} g_dwFlsIndex = FlsAlloc(NULL);
if (FLS_OUT_OF_INDEXES == g_dwFlsIndex){
_tprintf(_T("FlsAlloc出错(%d)"), GetLastError());
return -;
} //创建2个子纤程
for (int i = ; i< FIBER_COUNT;i++){
g_lpFiber[i] = CreateFiber(, FiberFunc, NULL);
if (g_lpFiber[i]==NULL){
_tprintf(_T("CreateFiber出错(%d)\n"), GetLastError());
return -;
}
} //轮流调度
for (int i = ; i < FIBER_COUNT;i++){
SwitchToFiber(g_lpFiber[i]);
} //删除纤程
for (int i = ; i < FIBER_COUNT;i++){
DeleteFiber(g_lpFiber[i]);
} FlsFree(g_dwFlsIndex); //纤程变回线程
ConvertFiberToThread(); _tsystem(_T("PAUSE"));
return ;
} 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 = ; //////////////////////////////////////////////////////////////////////////
//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 = ; x <= nCount;x++){
//检查线程的消息队列是否有新的消息(在同一个线程中运行的所有
//纤程共享该线程的消息队列)
if (HIWORD(GetQueueStatus(QS_ALLEVENTS))!=){
//UI纤程事件到达,先去暂停后台处理 ,转去处理UI事件
SwitchToFiber(pFiberInfo->pFiberUI); //UI事件处理完毕,后台继续计算
SetDlgItemText(pFiberInfo->hwnd, IDC_FIBER, TEXT("后台重算纤程"));
} //更新Answer
SetDlgItemInt(pFiberInfo->hwnd, IDC_ANSWER, x, FALSE); //为了夸大效果,睡眠一会儿
Sleep();
} //计算结束
pFiberInfo->bps = BPS_DONE; //重新调度UI线程。当线程正在运行并且没有UI事件可处理时,线程将进入睡眠状态
//(因为UI线程调用了WaitMessage)
SwitchToFiber(pFiberInfo->pFiberUI);
} //////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){
SetDlgItemInt(hwnd, IDC_COUNT, , FALSE);
return TRUE;
} void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotify){
switch (id)
{
case IDCANCEL:
PostQuitMessage();
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,,,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(, 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 ; //结束程序
}
//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
// TEXTINCLUDE
BEGIN
"resource.h\0"
END TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// Dialog
// IDD_COUNTER DIALOGEX , , ,
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "计数器"
FONT , "宋体", , , 0x86
BEGIN
LTEXT "计数到:",IDC_STATIC,,,,
EDITTEXT IDC_COUNT,,,,,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "当前值:",IDC_STATIC,,,,
LTEXT "",IDC_ANSWER,,,,
LTEXT "当前正在运行的纤程:",IDC_STATIC,,,,
LTEXT "Fiber",IDC_FIBER,,,,
END /////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
// #ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_COUNTER, DIALOG
BEGIN
LEFTMARGIN,
RIGHTMARGIN,
TOPMARGIN,
BOTTOMMARGIN,
END
END
#endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources
///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
第12章 纤程(Fiber)的更多相关文章
- Windows核心编程:第12章 纤程
Github https://github.com/gongluck/Windows-Core-Program.git //第12章 纤程.cpp: 定义应用程序的入口点. // #include & ...
- Windows核心编程 第十二章 纤程
第1 2章 纤 程 M i c r o s o f t公司给Wi n d o w s添加了一种纤程,以便能够非常容易地将现有的 U N I X服务器应用程序移植到Wi n d o w s中.U N I ...
- 基于纤程(Fiber)实现C++异步编程库(一):原理及示例
纤程(Fiber)和协程(coroutine)是差不多的概念,也叫做用户级线程或者轻线程之类的.Windows系统提供了一组API用户创建和使用纤程,本文中的库就是基于这组API实现的,所以无法跨平台 ...
- 继续了解Java的纤程库 – Quasar
前一篇文章Java中的纤程库 – Quasar中我做了简单的介绍,现在进一步介绍这个纤程库. Quasar还没有得到广泛的应用,搜寻整个github也就pinterest/quasar-thrift这 ...
- nodejs中的fiber(纤程)库详解
fiber/纤程 在操作系统中,除了进程和线程外,还有一种较少应用的纤程(fiber,也叫协程).纤程常常拿来跟线程做对比,对于操作系统而言,它们都是较轻量级的运行态.通常认为纤程比线程更为轻量,开销 ...
- 协程,纤程(Fiber),或者绿色线程(GreenThread)
纤程(Fiber),或者绿色线程(GreenThread) 面试官:你知道协程吗? 你:订机票的那个吗,我常用. 面试官:行,你先回去吧,到时候电话联系 ........ 很尴尬,但是事实是,很大一部 ...
- 纤程(FIBER)
Indy 10 还包含对纤程的支持.纤程是什么?简单来说,它也是 一个“线程”,但是它是由代码控制的,而不是由操作系统控制的.实际上,可以认为线程 是一个高级纤程.纤程和 Unix 用户线程(Unix ...
- 第 12 章 python并发编程之协程
一.引子 主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只用一个)情况下实现并发,并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作 ...
- Java 中的纤程库 – Quasar
来源:鸟窝, colobu.com/2016/07/14/Java-Fiber-Quasar/ 如有好文章投稿,请点击 → 这里了解详情 最近遇到的一个问题大概是微服务架构中经常会遇到的一个问题: 服 ...
随机推荐
- Angularjs,WebAPI 搭建一个简易权限管理系统 —— 基本功能演示(二)
目录 前言 Angularjs名词与概念 Angularjs 基本功能演示 系统业务与实现 WebAPI项目主体结构 Angularjs 前端主体结构 基本功能演示(二) 非常抱歉这个月实在太忙,一直 ...
- .NET Core常用配置文件示例
.NET Core相关地址: 1.官网:https://www.microsoft.com/net 2..NET Core:http://dotnet.github.io/3.Getting Star ...
- AT NEW F、AT END OF F注意事项
1.F只能是内表的第一个字段 2.AT NEW F.AT END OF F使用F之后内表内容会变为* 解决出现*的办法: FIELD-SYMBOLS:<ITAB> LIKE ITAB L ...
- GridView1事件
1 protected void GridView1_DataBinding(object sender, EventArgs e) { 该事件当服务器控件绑定数据时发生. }2 protected ...
- How does Web Analytics works under sharePoint 2010
[http://gokanx.wordpress.com/2013/06/15/how-does-web-analytics-works-under-sharepoint-2010/] You nee ...
- 转发离线安装 Android Studio 更新
1.在线更新 随着 Android Studio 的越来越完善与流行,无论从功能性,还是性能上,它正在成为广大 Android 开发者的首选.但是因为总所周知墙的原因,我们在 Android Stud ...
- 在Asp.net MVC中使用Authorization Manager (AzMan)进行Windows用户身份认证
背景 创建需要通过Windows用户进行身份认证的Asp.net MVC应用 要点 在Asp.net MVC应用基于Windows用户进行身份认证的方法有很多,如MVC自带的Windows认证就经常被 ...
- C#初级知识点整理及VS的简单使用
C#预处理器指令#define #undef 声明一个不需赋值的变量注意的一点事它必须放到using 上面,如 #define TEST using System.xxx; public class ...
- WebAPP与原生APP的交互设计区别
WebAPP和原生APP同为移动端,很少有研究这两项的交互区别,最近公司做了一次从原生APP到WebAPP(HTML5 )的移植,故总结一下期间遇到的问题及不同点总结. 从使用场景上,WebAPP用户 ...
- 【原创】大众点评监控平台cat的性能分析
由于工作的原因,或者说我们之前内部监控设计和实现有点不满足现有的研发需求,所以调研了一下大众点评开源出来的cat这一套监控系统. 今天我们就来实验一把,cat的客户端埋点在我们的程序流程中上报数据到c ...