C++异步IO

异步IO的用处

  首先,当我们创建一个线程用于进行阻塞函数和读取大型文件时,这条线程将会等待结果的反馈,阻塞在原地,那么我们可以说这条线程就有些浪费了,这就叫做同步IO。

  那我们有什么方法可以解放这个线程,让其先进行后面的操作,等待结果反馈再回头执行阻塞部分呢,答案就是异步IO,异步IO的原理就是将阻塞操作交给底层操作系统进行,等同于再开一条线程进行这些浪费时间的操作,以效能换取效率,而本该阻塞的线程继续执行后续代码,节省速率。

  由此,我们可以发现异步IO的便捷性,但又诞生了其他问题,该如何让线程知道阻塞已经结束,可以回头执行代码了,我们后面再慢慢看。

文件操作api

1.CreateFile 创建/打开文件

2. ReadFile 读取文件

3. WriteFile 写入文件

WriteFileEX

ReadFile

4.OVERLAPPED 重叠I/O结构体

首先我们先创建一个txt文件作为实验

在里面随便输入点字符

然后创建cpp文件


	 //给出 所有可能的访问权限
	//倒数第二个参数填  FILE_FLAG_OVERLAPPED   表明使用异步IO打开文件
	HANDLE file=CreateFile("test.txt", GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

	//当一个句柄由异步IO打开,那么
	// 句柄变为可等待对象,具有了激发态和非激发态


	//创建OVERLAPPED结构体存放异步的数据
	OVERLAPPED overlapped{ 0 };

	//创建缓冲区存放文件数据
	CHAR buff[0x100]{ 0 };

	ReadFile(file, buff, 0x100,NULL ,&overlapped);

注意在这里我们无需填入参数获取实际读取长度,因为由于异步IO,一时半会读取不完,那么实际读取参数就没用了

还有我们不能通过ReadFile的返回值判断读取是否成功,不具有参考价值

OVERLAPPED结构体

OVERLAPPED结构体是用于存储异步的数据的

  结构体声明:

typedef struct _OVERLAPPED {

ULONG_PTR Internal; //操作系统保留,指出一个和系统相关的状态

ULONG_PTR InternalHigh; //指出发送或接收的数据长度

union {

struct {

DWORD Offset; //文件传送的字节偏移量的低位字

DWORD OffsetHigh; //文件传送的字节偏移量的高位字

};

PVOID Pointer; //指针,指向文件传送位置

};

HANDLE hEvent; //指定一个I/O操作完成后触发的事件

} OVERLAPPED, *LPOVERLAPPED;

  在上述代码后面,我们可以执行我们需要做的其他事情,比如打印一些东西什么的,当完成所以其他事情时,我们就可以看看阻塞事件执行完了吗,这里也就是我们提到的让线程知道进行完成的方法

  在上面代码中,我们提到了,异步IO会让句柄变为可等待对象,具有了激发态和非激发态

  如此我们就不免想到了一个函数WaitForSingleObject,这个函数我们常用于等待线程的完成,我们因为异步IO,也可以等待该句柄的完成

//......
//省略的其他执行代码

//完结后
//等待完成。。。。。
WaitForSingleObject(file, -1);

DWORD numberOfBytes = 0;

//参数3 保存实际读写数
//参数4 是否等待结果出现,由于我们前面使用了WaitForSingleObject,所以这里已经完成,不需要等待
GetOverlappedResult(file, &overlapped, &numberOfBytes, FALSE);

printf("文件内容:%s", buff);

上述内容建立在单线程单文件读取的操作上,如果我们对一个文件多次读取,由于WaitForSingleObject参数1为相同文件句柄,我们怎么判断到底是哪个ReadFile的完成呢

这里就要提到OVERLAPPED里面我们上文忽略的参数

HANDLE hEvent

hEvent作为具有可等待对象,当然可以填充到WaitForSingleObject里面,同时由于我们可以自主给OVERLAPPED命名,他就变成了分辨的不二之选

#include<Windows.h>

#include<iostream>

int main()
{	
	HANDLE file = CreateFile("test.txt", GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

	OVERLAPPED overlapped{ 0 };

	OVERLAPPED overlapped2{ 0 };

	
	CHAR buff[0x100]{ 0 };
	CHAR buff2[0x100]{ 0 };
	ReadFile(file, buff, 0x100, NULL, &overlapped);
	//进行多次读取
	ReadFile(file, buff2, 0x100, NULL, &overlapped);

	//初始化事件
	overlapped.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
	overlapped2.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);

	//......
	//省略的其他执行代码

	//完结后
	//等待完成。。。。。
	WaitForSingleObject(overlapped.hEvent, -1);
	WaitForSingleObject(overlapped2.hEvent, -1);

	printf("文件内容:%s", buff);
	printf("文件内容:%s", buff2);

	return 0;
}

           但于此便诞生了另一个问题WaitForSingleObject(overlapped.hEvent, -1);
    WaitForSingleObject(overlapped2.hEvent, -1);很明显会造成阻塞,如此就有点得不偿失了

  我们需要一个方法,不需要手动的等待,让程序自己告诉我们,阻塞已经完成,那就需要使用回调函数

#include<Windows.h>

#include<iostream>



void WINAPI LpoverlappedCompletionRoutine(
     DWORD dwErrorCode,
	 DWORD dwNumberOfBytesTransfered,
	 LPOVERLAPPED lpOverlapped
)
{
	printf("1111");
}


int main()
{	
	HANDLE file = CreateFile("test.txt", GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

	OVERLAPPED overlapped{ 0 };

	OVERLAPPED overlapped2{ 0 };
 
	
	CHAR buff[0x100]{ 0 };
	CHAR buff2[0x100]{ 0 };

	//使用ReadFileEx添加回调函数
	ReadFileEx(file, buff, 10, &overlapped, LpoverlappedCompletionRoutine);
	//进行多次读取
	ReadFileEx(file, buff2, 5, &overlapped2, LpoverlappedCompletionRoutine);

	//让所在线程进入警醒状态
	     SleepEx(0, TRUE);

	
	return 0;
}

ReadFileEx将函数放入线程的的函数队列中,如果线程处于警醒模式,在结束后会自动按序运行队列函数,sleepE就会进入警醒模式

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值