您的位置:首页 > 编程语言 > C语言/C++

基于visual c++之windows核心编程代码分析(20) 纤程与线程的互相转换

2016-12-09 20:25 561 查看
在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:得到当前正在运行的纤程

 

[cpp] view
plain copy

**************************************/  

/* 预定义 */  

#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]);  

}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐