在Windows2000/XP中,纖程(fiber)相當于使用者級别的線程或輕程序.纖程由Win32庫函數支援,對核心是不可見的.纖程可以通過SwitchToFiber顯示至另一合作纖程,以實作合作纖程之間的協同.
纖程包含獨立的目态棧,寄存器狀态的控制資訊.目态控制的纖程轉接要求較高的程式設計經驗.由于纖程屬于目态對象,一個纖程被封鎖意味着所線上程被封鎖.應用程式可以通過ConvertThreadToFiber将線程轉換為纖程.與線程對比,纖程具有切換速度快的特點.
Microsoft公司給Windows添加了一種纖程,以便能夠非常容易地将現有的UNIX伺服器應用程式移植到Windows中。UNIX伺服器應用程式屬于單線程應用程式(由Windows定義),但是它能夠為多個客戶程式提供服務。換句話說, UNIX應用程式的開發人員已經建立了他們自己的線程結構庫,他們能夠使用這種線程結構庫來仿真純線程。該線程包能夠建立多個堆棧,儲存某些C P U寄存器,并且在它們之間進行切換,以便為客戶機請求提供服務。
顯然,若要取得最佳的性能,這些UNIX應用程式必須重新設計,仿真的線程庫應該用Windows提供的純線程來替代。然而,這種重新設計需要花費數月甚至更長的時間才能完成,是以許多公司首先将它們現有的UNIX代碼移植到Windows中,這樣就能夠将某些應用軟體推向Windows市場。
使用纖程
線程是在Windows核心中實作的,纖程是在使用者模式下實作的,核心對纖程一無所知,核心會根據我們定義的算法來對纖程進行排程。
一個線程可以包含一個或多個纖程。
轉化線程為纖程
使用纖程的第一個步驟是将已有的線程轉換為一個纖程。ConvertThreadToFiber這個函數會為纖程的上下文配置設定記憶體,這個上下文的構成是:
# 一個使用者自定義的值
# 結構化異常處理鍊的頭
# 纖程棧的頂部和底部的記憶體位址
# 某些CPU寄存器,其中包括棧指針、指令指針以及其他寄存器
當我們配置設定了纖程執行上下文并對其進行初始化之後,還必須将執行上下文的位址與線程關聯起來。這樣我們就将線程轉換成了一個纖程,該纖程在這個線程中執行。
其實,除非我們打算建立更多的纖程,并讓它們在同一個線程中運作,否則沒有理由将一個線程轉換為纖程。
CreateFiber:建立一個纖程
SwitchToFiber:調用一個纖程(同一個線程中,同一時刻隻能執行一個纖程)
DeleteFiber:通常為一個纖程調用,來删除另一個纖程
GetCurrentFiber:得到目前正在運作的纖程
**************************************/
/* 預定義 */
#define _WIN32_WINNT 0x0501
/* 頭檔案 */
#include <windows.h>
#include <stdio.h>
/* 函數聲明 */
VOID WINAPI ReadFiberFunc( LPVOID lpParameter );
VOID WINAPI WriteFiberFunc( LPVOID lpParameter );
/* 結構定義 */
// 用于向纖程傳遞參數
// 本執行個體是使用讀、寫檔案來示範纖程的排程
// 使用者可根據實際情況自行定義
typedef struct _FIBERDATASTRUCT
{
DWORD dwParameter; // 預留給向纖程傳遞待定參數
DWORD dwFiberResultCode; // GetLastError() 值
HANDLE hFile; // 纖程所操作檔案的句柄
DWORD dwBytesProcessed; // 已經處理了的位元組
}FIBERDATASTRUCT, *LPFIBERDATASTRUCT;
/* 常量定義 */
#define RTN_OK 0 // 傳回值 成功
#define RTN_USAGE 1 // 傳回值 參數不正确
#define RTN_ERROR 2 // 傳回值 錯誤
#define BUFFER_SIZE 32768 // 緩沖區大小
#define FIBER_COUNT 3 // 主纖程、讀纖程、寫纖程,共三個
#define PRIMARY_FIBER 0 // 主纖程
#define READ_FIBER 1 // 讀纖程
#define WRITE_FIBER 2 // 寫纖程
LPVOID g_lpFiber[FIBER_COUNT]; // 纖程位址的數組
LPBYTE g_lpBuffer; // 緩沖區
DWORD g_dwBytesRead; // 已讀的位元組
int main( int argc, char *argv[] )
{
LPFIBERDATASTRUCT fs;
// 用法說明
if (argc != 3)
{
printf("Usage: %s <SourceFile> <DestinationFile>\n", argv[0]);
return RTN_USAGE;
}
// 配置設定FIBERDATASTRUCT空間,FIBER_COUNT個
fs = (LPFIBERDATASTRUCT)HeapAlloc(
GetProcessHeap(), 0,
sizeof(FIBERDATASTRUCT) * FIBER_COUNT);
if (fs == NULL)
{
printf("HeapAlloc error! (rc%=lu)\n", GetLastError());
return RTN_ERROR;
}
// 配置設定讀、寫緩沖區
g_lpBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
if (g_lpBuffer == NULL)
{
printf("HeapAlloc error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 打開源檔案,将句柄指派給fs結構的hFile成員,使纖程可以使用該句柄
fs[READ_FIBER].hFile = CreateFile(
argv[1], GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, NULL
);
if (fs[READ_FIBER].hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 打開目标檔案
fs[WRITE_FIBER].hFile = CreateFile(
argv[2], GENERIC_WRITE,
0, NULL, CREATE_NEW,
FILE_FLAG_SEQUENTIAL_SCAN, NULL
);
if (fs[WRITE_FIBER].hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 将主線程切換為纖程,為主纖程,隻有轉換為纖程後才可以切換至其他纖程
g_lpFiber[PRIMARY_FIBER]=ConvertThreadToFiber(&fs[PRIMARY_FIBER]);
if (g_lpFiber[PRIMARY_FIBER] == NULL)
{
printf("ConvertThreadToFiber failed! rc=%lu\n", GetLastError());
return RTN_ERROR;
}
// 主纖程資料
fs[PRIMARY_FIBER].dwParameter = 0;
fs[PRIMARY_FIBER].dwFiberResultCode = 0;
fs[PRIMARY_FIBER].hFile = INVALID_HANDLE_VALUE;
// 建立讀纖程
g_lpFiber[READ_FIBER]=CreateFiber(0,ReadFiberFunc,&fs[READ_FIBER]);
if (g_lpFiber[READ_FIBER] == NULL)
{
printf("CreateFiber error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
// 将纖程指針作為參數傳給纖程,沒有實際意義,為了顯示相關資訊時差別各纖程
fs[READ_FIBER].dwParameter = (DWORD)g_lpFiber[READ_FIBER];
// 建立寫纖程
g_lpFiber[WRITE_FIBER]=CreateFiber(0,WriteFiberFunc,&fs[WRITE_FIBER]);
if (g_lpFiber[WRITE_FIBER] == NULL)
{
printf("CreateFiber error! (rc=%lu)\n", GetLastError());
return RTN_ERROR;
}
fs[WRITE_FIBER].dwParameter = (DWORD)g_lpFiber[WRITE_FIBER];
// 切換到讀程式執行
SwitchToFiber(g_lpFiber[READ_FIBER]);
// 由讀纖程獲寫纖程切換回主纖程
// 顯示相關資訊
printf("ReadFiber result == %lu Bytes Processed == %lu\n",
fs[READ_FIBER].dwFiberResultCode, fs[READ_FIBER].dwBytesProcessed);
printf("WriteFiber result == %lu Bytes Processed == %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);
HeapFree(GetProcessHeap(), 0, g_lpBuffer);
HeapFree(GetProcessHeap(), 0, fs);
return RTN_OK;
}
VOID WINAPI ReadFiberFunc( LPVOID lpParameter )
{
LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
// 判斷參數
if (fds == NULL)
{
printf("Passed NULL fiber data. Exiting current thread.\n");
return;
}
// 顯示纖程資訊
printf("Read Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
// 初始化處理的位元組數為0
fds->dwBytesProcessed = 0;
// 循環讀
while (1)
{
if (!ReadFile(fds->hFile, g_lpBuffer, BUFFER_SIZE,
&g_dwBytesRead, NULL))
{
break;
}
// 判斷檔案是否已經讀完
if (g_dwBytesRead == 0) break;
// 已經處理的位元組,累加
fds->dwBytesProcessed += g_dwBytesRead;
// 讀一次後切換到寫纖程,将讀出的資料寫入到目标檔案
printf("Switch To Write");
SwitchToFiber(g_lpFiber[WRITE_FIBER]);
}
// 讀操作完成,準備交出執行,傳回到主纖程中
fds->dwFiberResultCode = GetLastError();
SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}
VOID WINAPI WriteFiberFunc( LPVOID lpParameter )
{
LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
DWORD dwBytesWritten;
// 判斷參數
if (fds == NULL)
{
printf("Passed NULL fiber data. Exiting current thread.\n");
return;
}
// 顯示纖程資訊
printf("Write Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
// 初始化,注意和讀纖程的不同
fds->dwBytesProcessed = 0;
fds->dwFiberResultCode = ERROR_SUCCESS;
while (1)
{
// 寫入資料
if (!WriteFile(fds->hFile, g_lpBuffer, g_dwBytesRead,
&dwBytesWritten, NULL))
{
// 如果發生錯誤,退出循環
break;
}
fds->dwBytesProcessed += dwBytesWritten;
// 寫入完成,切換到讀纖程
printf("Switch To Write");
SwitchToFiber(g_lpFiber[READ_FIBER]);
}
// 出錯,切換到主纖程
// 如果寫操作不出錯,是不可能由寫纖程切換回主纖程的
fds->dwFiberResultCode = GetLastError();
SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}