纤程(Fiber)是 windows 操作系统提供的概念。那什么是纤程呢?
纤程是一种比线程更轻量级的执行单元,它可以在一个线程中切换执行,不需要操作系统内核的干预。纤程可以用来实现异步任务,避免了创建新线程的开销。纤程也叫做协程(coroutine),是一种用户态的多任务机制。
协程与纤程主要的区别点:
一个简单的纤程程序,创建两个纤程并在它们之间切换:
#include "pch.h"
#include <IOStream>
#include <windows.h>
#include <tchar.h>
#define FIBER_COUNT 2
LPVOID g_lpFiber[FIBER_COUNT] = {};
VOID WINAPI FiberFun(LPVOID pParam) //纤程函数的返回类型为VOID,并不是因为返回值没有意义,而是因为这个函数不应该返回!
{
int nFiberIndex = (int)pParam;
while (true)
{
std::cout << "Fiber" << nFiberIndex << std::endl;
SwitchToFiber(g_lpFiber[1 - nFiberIndex]); //切换到另一个纤程
}
}
int _tmAIn(int argc, _TCHAR* argv[])
{
LPVOID lpMainFiber = ConvertThreadToFiber(NULL); //将当前线程转换为主纤程
if (lpMainFiber == NULL)
{
std::cout << "ConvertThreadToFiber failed" << std::endl;
return -1;
}
for (int i = 0; i < FIBER_COUNT; i++)
{
g_lpFiber[i] = CreateFiber(0, FiberFun, (LPVOID)i); //创建子纤程
if (g_lpFiber[i] == NULL)
{
std::cout << "CreateFiber failed" << std::endl;
return -1;
}
}
SwitchToFiber(g_lpFiber[0]); //切换到第一个子纤程
for (int i = 0; i < FIBER_COUNT; i++)
{
DeleteFiber(g_lpFiber[i]); //删除子纤程
}
ConvertFiberToThread(); //将主纤程转换回线程
return 0;
}
一个使用纤程实现协同程序的例子:
#include <windows.h>
#include <stdio.h>
#define MAX_FIBERS 3
Dword dwCounter;
void WINAPI MyFunc(LPVOID lpParameter)
{
DWORD dwIndex;
dwIndex = *(DWORD *)lpParameter;
while(TRUE)
{
printf("dwCounter=%d,dwIndex=%dn",dwCounter,dwIndex);
dwCounter++;
SwitchToFiber(lpParameter);
}
}
void main()
{
LPVOID lpMainAddress;
LPVOID lpAddress[MAX_FIBERS];
DWORD dwParameter[MAX_FIBERS];
int i;
lpMainAddress=ConvertThreadToFiber(NULL);
for(i=0;i<MAX_FIBERS;i++)
{
dwParameter[i]=i+1;
lpAddress[i]=CreateFiber(0,(LPFIBER_START_ROUTINE)MyFunc,&dwParameter[i]);
}
for(i=0;i<10;i++)
SwitchToFibers(lpAddress[i%MAX_FIBERS]);
for(i=0;i<MAX_FIBERS;i++)
DeleteFibers(lpAddress[i]);
printf("endn");
}
尽管协程的概念早于线程,但协程的实现并不是所有操作系统原生支持的。目前,很多编程语言都是通过自己的运行时环境来模拟协程,利用线程技术来实现协程的调度。这些语言中,像 golang 这样的语言在实现上比较成熟,可以支持大量的协程同时执行,这也是 golang 能够处理高并发的原因之一。
在 golang 中,协程的实现是基于线程的,它维护了一个协程队列,由多个线程来负责执行协程队列中的任务。当一个协程在执行过程中遇到了阻塞操作,比如等待 IO 数据返回,它会被放入一个阻塞队列中,等待 IO 数据返回后再继续执行。在这个过程中,当前线程会去执行队列中的其他协程,从而实现协程之间的切换。