在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]); }