DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 


简介:
我们本篇先讲解下芊程,下一篇再介绍协程,因为有了芊程的概念后,我们再讲解协程,就好理解了。

 

一、前言

芊程(fiber)是windows系统中的概念。当我们需要异步执行一些任务时,常见的作法就是开启一个工作线程,在工作线程中执行我们的任务。但是这样存在两个问题:

  • 对于线程的调度是由操作系统内核控制的,所以我们无法确认操作系统何时会运行或挂起该线程。
  • 对于一些轻量级的任务,创建一个新的线程去做,消耗较大。

那么有没有一种机制,既能新建线程执行任务,又没有新建线程消耗那么大呢?有,这就是芊程。

二、芊程(fiber)

1、线程中使用芊程

在windows系统中,一个线程可以有多个芊程,用户可以根据需要在各个芊程之间自由切换。如果需要在某个线程中使用芊程,则必须将该线程切换成芊程模式,这可以通过调用如下API函数实现。

 PVOID ConvertThreadToFiber(LPVOID lpParameter)

这个函数不仅仅将当前线程切换成芊程模式,也可以得到线程中的第1个芊程。我们可以通过这个函数的返回值来引用和操作芊程,这个线程是线程中的主芊程。但是这个主芊程无法指定芊程函数,所以什么也做不了。

简单了解:
在不同的芊程之间切换时,也会涉及到芊程上下面的切换,包括cpu寄存器数据的切换。默认情况下,x86系统的cpu浮点状态信息不属于cpu寄存器,不会为每个芊程都维护一份,因此如果在我们的芊程中执行浮点操作,则会导致数据被破坏。为了禁用这种行为,我需要用到convertThreadToFiberEx 函数

LPVOID ConvertThreadToFiberEx(
  [in, optional] LPVOID lpParameter,
  [in]           DWORD  dwFlags
);

2、获取当前芊程数据

我们可以通过参数lpParameter向主芊程传递数据,使用如下API函数获取当前芊程的数据。

PVOID GetFiberData();

3、从芊程切回线程

将线程从芊程模式切回至默认的线程模式时,会调用API函数

BOOL ConvertFiberToThread();

4、创建新的芊程

因为默认的主芊程什么都做不了,所以我们在需要时要创建新的芊程,这会用到API函数

LPVOID CreateFiber(
  [in]           SIZE_T                dwStackSize,
  [in]           LPFIBER_START_ROUTINE lpStartAddress,
  [in, optional] LPVOID                lpParameter
);

和创建线程的函数类似,参数dwStackSize指定芊程栈的大小,如果使用默认大小,这该值设置为0即可。我们可以将CreateFiber函数的返回值作为操作芊程的句柄。
芊程函数签名如下

VOID LPFIBER_START_ROUTINE(LOVOID  lpParameter)

5、删除芊程对象

当不需要使用芊程时,需要调用DeleteFiber删除芊程对象

void DeleteFiber(
  [in] LPVOID lpFiber
);

6、在不同芊程间切换

在不通过的芊程之间切换时,会调用API函数

void SwitchToFiber([in] LPVOID lpFiber);

7、芊程局部存储

和线程存在线程局部存储一样,芊程也可以有自己的局部存储——芊程局部存储,获取和设置芊程局部存储时,会调用API函数

DWORD FlsAlloc(
  [in] PFLS_CALLBACK_FUNCTION lpCallback
);
BOOL FlsFree(
  [in] DWORD dwFlsIndex
);
BOOL FlsSetValue(
  [in]           DWORD dwFlsIndex,
  [in, optional] PVOID lpFlsData
);
PVOID FlsGetValue(
  [in] DWORD dwFlsIndex
);

三、demo

以下代码只有一个主线程,主线程在切换到新建的芊程SwitchToFiber。由于在新建的芊程函数中是一个while无限循环,所以,如果协程中如果没有SwitchToFiber切回主协程,代码将在while中无限循环下去。

  • 代码
#include <iostream>
#include<Windows.h>
#include<string>

using namespace std;

LPVOID mainWorkerFiber = NULL;
LPVOID pWorkerFiber = NULL;

void WINAPI workerFiberProc(LPVOID lpFiberParameter)
{
	while (true)
	{
		// 假设这是一个很耗时的操作
		SYSTEMTIME st;
		GetLocalTime(&st);

		cout << st.wYear << "-" << st.wMonth << "-" << st.wDay << " " << st.wHour << ":" << st.wMinute << ":" << st.wSecond <<" "<<st.wMilliseconds<< endl;

		//切回主协程
		SwitchToFiber(mainWorkerFiber);
	}
}

int main()
{
	mainWorkerFiber = ConvertThreadToFiber(NULL);

	int index = 0;
	while (index < 10)
	{
		++index;

		pWorkerFiber = CreateFiber(0, workerFiberProc, NULL);
		if (NULL == pWorkerFiber)
			return - 1;
		

		cout << "time not set"<<endl;
		Sleep(2000);
		SwitchToFiber(pWorkerFiber);
	}

	DeleteFiber(pWorkerFiber);
	ConvertFiberToThread();

	return 0;
}
  • 切回主协程SwitchToFiber未注释掉,输出如下
  • 切回主协程SwitchToFiber注释掉,输出如下

四、芊程和协程

  • 芊程从本质上来说就是协程(coroutine),Windows的芊程技术让单个线程能按照用户的意愿像线程一样自由切换,且没有线程切换那么大的开销和不可控性。

参考:
1、《c++服务器开发精髓》 张远龙 著;;
2、微软官网:https://learn.microsoft.com/zh-cn/windows/win32/api/winbase/nf-winbase-createfiber

 
posted on 2023-03-16 16:58  DoubleLi  阅读(153)  评论(0编辑  收藏  举报