第12章 纤程(Fiber)

第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

#include

#include

#include

//////////////////////////////////////////////////////////////////////////

#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

#include

#include

#include

//////////////////////////////////////////////////////////////////////////

#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

#include

#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

相关风暴

30公顷等于多少平方米?
安卓怎么安装365BET

30公顷等于多少平方米?

🌀 09-13 🌊 阅读 5491
镖师等级如何短时间内快速达到满级?
英国手机版365

镖师等级如何短时间内快速达到满级?

🌀 07-24 🌊 阅读 9025
米 到 英尺 轉換器
安卓怎么安装365BET

米 到 英尺 轉換器

🌀 01-22 🌊 阅读 4127