- 什么是重叠IO
重叠模型是一种异步IO模型。多线程结构中就考虑到采用异步的方式进行设备读写操作,即我们告诉系统对设备的读写数据,而同时应用程序的其他代码继续执行,直到获取设备操作完毕的系统通知。
- 重叠IO的作用
我们在这里做一下对使用重叠IO和非重叠IO的解释:
非重叠IO的情况:
在Windows中,我们通常使用CreateFile、WriteFile、ReadFile等函数对文件进行操作时,会让我们觉得有些“慢”,那这是为什么呢?因为这些函数的工作方式是等待输入或输出操作结束后才会返回,并且对于这些IO设备来说,它们的读写效率是非常低下的,如果要等到这些设备执行完毕才往后执行的话,那你当前的程序岂不是等的花都谢了。如果频繁的需要对文件进行读写操作的话,那可想而知效率会是多么的低。对于这种方式,通常被Windows系统称之为“同步”的方式。
使用重叠IO的情况:
那么我们要如何提高效率呢?当然是用我们这里提到的重叠IO来操作了。重叠IO是一种“异步”执行方式,所以当你调用一个IO函数后,该函数会立即返回,而你的当前程序就会继续往下执行,所以你需要建立一个“可警告(alert able)”的线程来等待接收IO操作完成的通知,这样调用IO函数与IO函数的完成返回就成了“异步”方式执行。对于调用者来说,它的目标就集中到了整个程序逻辑的合理性问题上,而不用去关心IO操作的结果。
- 重叠IO详解
重叠IO?为什么要叫重叠IO?叫异步IO不好吗?可能这个名字恰恰显示了这种IO操作方式的精髓“重叠”吧。我们来谈谈重叠这个概念:这里假设我们有多个需要频繁进行写操作的日志系统,那么我们在对其中一个进行IO操作时,是否需要等待道这个IO操作的完成通知后再进行下一个日志系统的写入?其实不用的,这时我们可以直接调用下一个IO操作,而这些IO操作是不是就可以看做堆叠在一起呢,然后在等待线程中等待这些IO操作的完成。或许这就是所谓的重叠的意思吧!这些“重叠”在一起的IO操作,将按照一定的顺序被完成,但是它们的完成通知并不是严格按照顺序回调,尤其是在多线程环境中,回调基本是随机的。调用顺序,和完成回调顺序是完全不同的两个概念,这一点一定要区别清楚。
在Windows中有一个重叠结构——OVERLAPPED,通常在你调用的API参数中如果出现了改字眼,那么你几乎可以断定这个操作用到了重叠IO的技术。
通常情况下我们在实际编程中喜欢把OVERLAPPED结构进行重新封装,例如下面我在项目中用到的代码:
typedef struct
{
OVERLAPPED Overlapped;
CHAR FileName[MAX_PATH];
CHAR FilePath[MAX_PATH];
HANDLE hFile;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
像这里,我将OVERLAPPED封装到了结构体里面,并将OVERLAPPED设置为该结构体的第一个数据成员,其他的数据成员可任意由开发者编写,你在重叠IO中需要传输什么数据都可以通过该结构体进行传输。看下边代码:
LPPER_IO_OPERATION_DATA pOverlapped;
PostQueuedCompletionStatus(MonitorManager::GetInstance()->GetMonitorFolder()->GetIOCPHandle()
, 0, READFILE_KEY, &(pOverlapped->Overlapped));
我在使用重叠IO时用pOverlapped->Overlapped指针的方式使用重叠结构,并在后续接收时强制转换回封装后的重叠结构,代码如下
while (TRUE)
{
if (GetQueuedCompletionStatus(pMonitorFolder->m_hIOCP, &dwTransCount, &CompletionKey, &pOverlapped, INFINITE))
{
switch (CompletionKey)
{
case READFILE_KEY:
{
pOverlappedInfo = (LPPER_IO_OPERATION_DATA)pOverlapped;
// ... 操作
break;
}
case WRITEFILE_KEY:
{
pOverlappedInfo = (LPPER_IO_OPERATION_DATA)pOverlapped;
break;
}
default:
break;
}
}
}
通过上面的方式我们就可以在获取到完成端口后使用重新定义的重叠结构里面的其他参数了。
- 参考文献
https://www.cnblogs.com/carekee/articles/5437827.html