windows核心编程,纤程使用
Microsoft公司给Windows添加了一种纤程,以便能够非常容易地将现有的 UNIX服务器应用程序移植到Windows中。UNIX服务器应用程序属于单线程应用程序(由Windows定义),但是它能够为多个客户程序提供服务。换句话说,UNIX应用程序的开发人员已经创建了他们自己的线程结构库,他们能够使用这种线程结构库来仿真纯线程。该线程包能够创建多个堆栈保存某些CPU寄存器,并且在它们之间进行切换,以便为客户机请求提供服务。显然,若要取得最佳的性能,这些 UNIX应用程序必须重新设计,仿真的线程库应该用Windows提供的纯线程来替代。然而,这种重新设计需要花费数月甚至更长的时间才能完成因此许多公司首先将它们现有的 UNIX代码移植到Windows中,这样就能够将某些应用软件推
向Windows市场。当你将UNIX代码移植到Windows中时,一些问题就会因此而产生。尤其是 Windows管理线程的内存栈的方法要比简单地分配内存复杂得多。 Windows内存栈开始时的物理存储器的容量比较小,然后根据需要逐步扩大。这个过程在第 16章"线程的堆栈"中详细介绍。由于结构化异常处理机制的原因,代码的移植就更加复杂了。
为了能够更快和更正确地将它们的代码移植到 Windows中,Microsoft公司在操作系统中添加了纤程。本章将要介绍纤程的概念、负责操作纤程的函数以及如何利用纤程的特性。要记住如果有设计得更好的使用Windows自身线程的应用程序,那么应该避免使用纤程。
文章目录
cpp
/*
12_Counter.cpp
纤程使用的
*/
#include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */
#include <WindowsX.h>
#include <tchar.h>
#include "Resource.h"
#include <StrSafe.h>
/
//后台处理过程可能的状态
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;
/
VOID WINAPI LogMessage(PVOID pFlsValue);//FlsAlloc中指定的回调函数
void WINAPI FiberFunc(LPVOID lpParamter);
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PTSTR pszCmdLine, int)
{
//后台处理的纤程对象
PVOID pFiberCounter = NULL;
//主线程转为主线程
g_FiberInfo.pFiberUI = ConvertThreadToFiber(NULL);
//如果FLS插槽正在使用中,则在删除线程,线程退出已经释放FLS所有时调用FLS
g_dwSlot = FlsAlloc(LogMessage);//FLS索引时调用FlsCallback
FlsSetValue(g_dwSlot, (PVOID)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 后台处理状态
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((PVOID)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;
}// 打开后台处理状态
}//不存在UI消息
} // 当窗口仍然存在时
DestroyWindow(g_FiberInfo.hwnd);
return 0;
}
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);
}
VOID WINAPI LogMessage(PVOID pFlsValue)
{
TCHAR szMsg[MAX_PATH];
// Check if we are in a fiber because this function can be called
// outside a fiber execution
if (IsThreadAFiber())
{
PVOID pFiberData = GetCurrentFiber();
PCTSTR pszFlsValue = (PCTSTR)FlsGetValue(g_dwSlot);
StringCchPrintf(szMsg, _countof(szMsg), TEXT("[0x%x - %s] %s\n"),
pFiberData,
(pszFlsValue == NULL) ? TEXT("'Null value'") : (PCTSTR)pszFlsValue,
(pFlsValue == NULL) ? TEXT("'Null value'") : (PCTSTR)pFlsValue);
}
else
{
StringCchCopy(szMsg, _countof(szMsg), TEXT("No more a fiber...\n"));
}
OutputDebugString(szMsg);
}
void WINAPI FiberFunc(PVOID pvParam) {
PFIBERINFO pFiberInfo = (PFIBERINFO)pvParam;
FlsSetValue(g_dwSlot, TEXT("Computation"));
LogMessage(TEXT("entering computation..."));
// Update the window showing which fiber is executing.
SetDlgItemText(pFiberInfo->hwnd, IDC_FIBER, TEXT("Recalculation"));
// Get the current count in the EDIT control.
int nCount = GetDlgItemInt(pFiberInfo->hwnd, IDC_COUNT, NULL, FALSE);
// Count from 0 to nCount, updating the STATIC control.
for (int x = 0; x <= nCount; x++) {
// UI events have higher priority than counting.
// If there are any UI events, handle them ASAP.
if (HIWORD(GetQueueStatus(QS_ALLEVENTS)) != 0) {
// The UI fiber has something to do; temporarily
// pause counting and handle the UI events.
SwitchToFiber(pFiberInfo->pFiberUI);
// The UI has no more events; continue counting.
SetDlgItemText(pFiberInfo->hwnd, IDC_FIBER, TEXT("Recalculation"));
}
// Update the STATIC control with the most recent count.
SetDlgItemInt(pFiberInfo->hwnd, IDC_ANSWER, x, FALSE);
// Sleep for a while to exaggerate the effect; remove
// the call to Sleep in production code.
Sleep(200);
}
// Indicate that counting is complete.
pFiberInfo->bps = BPS_DONE;
// Reschedule the UI thread. When the UI thread is running
// and has no events to process, the thread is put to sleep.
// NOTE: If we just allow the fiber function to return,
// the thread and the UI fiber die -- we don't want this!
SwitchToFiber(pFiberInfo->pFiberUI);
}
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) {
chSETDLGICONS(hwnd, IDI_COUNTER);
SetDlgItemInt(hwnd, IDC_COUNT, 0, FALSE);
return(TRUE);
}
///
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) {
switch (id) {
case IDCANCEL:
PostQuitMessage(0);
break;
case IDC_COUNT:
if (codeNotify == EN_CHANGE) {
// When the user changes the count, start the
// background processing over from the beginning.
g_FiberInfo.bps = BPS_STARTOVER;
}
break;
}
}