【免杀前置课——Windows编程】十四、异步IO——什么是异步IO、API定位问题、APC调用队列

异步IO

当我们读取一个文件时,一般情况下,线程是阻塞的,也就是说,当前线程在等待文件读取操作结束,这种方式叫同步IO
Windows 在系统底层为用户实现了另外一种高效的机制,叫重叠I/0,又称作异步I/0。异步I/0提供了这样一种功能,当用户读取文件的时候,读取文件函数会立马返回结果不会阻塞线程,但是实际上文件并没有读取完,而是交给了系统底层自动去处理,这样文件的读取操作就不会阻塞住你的线程。但是这种引发了一个问题,我们如何才能知道文件已经读取完毕了呢? I

异步I/0注意事项:

一旦一个句柄是以异步I/0的方式打开的,那么:
1、句柄变为可等待的对象,就是说,它具有了激发态和非激发态。
2、文件指针这个东西就失效了,需要用overlapped结构体中的offset表示读取或写入的位置。

在这里插入图片描述
在这里插入图片描述

定位问题

异步IO存在问题,如果不止通过一个句柄进行读操作还有其他操作,则GetOverlappedResult函数无法定位到是哪一个操作。

#include<iostream>
#include<Windows.h>

int main() 
{
	//用异步IO方式打开一个文件
	HANDLE hFile = CreateFileW(L"D:\\code\\VisualStudio2022\\异步Io\\异步Io\\test.txt", GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);//FILE_FLAG_OVERLAPPED异步IO
	CHAR buff[0X100]{ 0 };
	OVERLAPPED overlapped{ 0 };
	//读取文件内容
	ReadFile(hFile, buff, 0X100, NULL, &overlapped);
	DWORD numberOfBytes = 0;
	WaitForSingleObject(hFile, -1);
	GetOverlappedResult(hFile, &overlapped, &numberOfBytes, FALSE);
	printf("文件内容:%s\n", buff);
	printf("实际完成IO数:%d\n", numberOfBytes);
	CloseHandle(hFile);
	return 0;
}

在这里插入图片描述
解决无法精确定位是哪个API完成的问题。新问题是,到最后还是要调用WaitForSingleObject函数,如果操作过大还是会导致卡顿,无法流畅体验。

#include<iostream>
#include<Windows.h>

int main()
{
	HANDLE hFile = CreateFile(L"D:\\code\\VisualStudio2022\\异步Io\\异步Io\\test.txt", GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	OVERLAPPED overlapped{ 0 };
	OVERLAPPED overlapped1{ 0 };
	overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	overlapped1.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	char buff[0X100]{ 0 };
	char buff1[0X100]{ 0 };
	ReadFile(hFile, buff, 10, NULL, &overlapped);
	ReadFile(hFile, buff1, 20, NULL, &overlapped1);
	//精确地判断哪个结束
	WaitForSingleObject(overlapped.hEvent, -1);
	WaitForSingleObject(overlapped1.hEvent, -1);
	printf("%s\n", buff);
	printf("%s\n", buff1);

	return 0;
}

总解决方案

APC调用队列

每个线程都维护了一个APC(异步过程调用)队列,队列中的每一项都是函数,当一个线程处于可警醒(闲暇)状态时,线程会遍历自己的APC队列并调用所有的函数,直到结束,再恢复执行。
每当有一个异步IO请求完成的时候,ReadFileEx,就会像APC队列中添加相应的函数和对应的参数,添加的顺序和投递的顺序不一定相同,哪个先处理完就先投递哪个。

通过ReadFileEx函数,让每一个异步IO完成后都向APC队列中加入函数及相应参数,来判断线程顺序的同时不使用WaitForSingleObject等待激发且抢占不影响流畅性。

#include<iostream>
#include<Windows.h>

void WINAPI overLappedCompletionProc(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
	if (lpOverlapped->hEvent == (HANDLE)0X100)
	{
		printf("1完成了\n");
	}
	else {
		printf("2完成了\n");
	}
}

int main()
{
	HANDLE hFile = CreateFile(L"D:\\code\\VisualStudio2022\\异步Io\\异步Io\\test.txt", GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	OVERLAPPED overlapped{ 0 };
	OVERLAPPED overlapped1{ 0 };
	//overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	//overlapped1.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	overlapped.hEvent = (HANDLE)0X100;
	overlapped1.hEvent = (HANDLE)0X200;
	char buff[0X100]{ 0 };
	char buff1[0X100]{ 0 };
	//当有一个异步IO请求完成后,就会向APC队列中添加相应的函数和对应的参数
	ReadFileEx(hFile, buff, 10, &overlapped, overLappedCompletionProc);
	ReadFileEx(hFile, buff1, 20, &overlapped1, overLappedCompletionProc);//当进程处于激发态时触发,通过Sleep让进程处于激发态
	SleepEx(0, TRUE);
	//ReadFile(hFile, buff, 10, NULL, &overlapped);
	//ReadFile(hFile, buff1, 20, NULL, &overlapped1);
	//精确地判断哪个结束
	//WaitForSingleObject(overlapped.hEvent, -1);
	//WaitForSingleObject(overlapped1.hEvent, -1);
	printf("%s\n", buff);
	printf("%s\n", buff1);

	return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webfker from 0 to 1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值