欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

完成端口读取文件

程序员文章站 2022-06-30 08:06:23
...

完成端口首先得了解什么是重叠io OVERLAPPED I/O 异步APC

下面的例子只告诉你怎么写这个程序, 不是告诉你理论,即如果在服务器上为什么不使用一个线程一个客户的模型

CreateIoCompletionPort  创建一个完成端口. 这个函数有一般需要调用2次

     第一次:HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 线程数量);

            如此创建了一个完成端口; 这个完成端口可以与其他有OVERLAPPED属性的HANDLE 关联

 

    第二次: CreateIoCompletionPort(hFile, iocp,(DWORD)key, 0); 

            把第一个参数的 handle 与 完成端口 iocp 绑定在一起, 第三个参数自定义数据

            需要注意 第一个参数的handle 必须是一个可重叠的 , 即创建handle时有 OVERLAPPED 属性的.

 

GetQueuedCompletionStatus  一般是在线程中调用, 用于返回结果, 就好像 GetOverlappedResult 一样.

  注:一般在线程中调用的意思是 , 完成端口一般将创建一组线程池来处理获取的结果

当在线程中调用了 GetQueuedCompletionStatus (iocp , ....)  之后 , 此线程相当于已归属于 对应的iocp(完成端口了).

意思是,此线程将监听完成端口的情况,一旦任务完成就会返回

 

PostQueuedCompletionStatus: 模拟已经完成的请求. 换句话说,你可以把这个函数理解成一个想要结束线程的函数;

 

 

typedef struct
{
	HANDLE hFile;
} cp_key;                  //一个自定义数据 ,随便填吧

typedef struct 
{
	OVERLAPPED overlap;
	char buf[1024];
} cp_overlapped;        //overlapped 放第一个, 相当于一个OVERLAPPED结构


unsigned int __stdcall io_thread(void *param)
{
	HANDLE iocp = (HANDLE)param;
	_tprintf(TEXT("线程创建完毕 iocp:%p\n"), iocp);
	DWORD bytesRead = 0;
	cp_key * key = 0;
	cp_overlapped * s = 0;
	DWORD ret = 0;

	while (1)
	{
        
        //等待完成端口的响应
		ret = GetQueuedCompletionStatus(iocp, &bytesRead, (LPDWORD)&key, (LPOVERLAPPED*)&s, INFINITE);

        //退出线程
		if (key == 0 && s == 0){
			_tprintf(TEXT("exit"));
			break;
		}
		_tprintf(TEXT("byteread:%d, key:%p,ret :%d\n"), bytesRead, key->hFile, ret);
		_tprintf(TEXT("overlapped  offset:%d\n"), s->overlap.Offset);
		_tprintf(TEXT("%s\n"),s->buf);
	}
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	_tsetlocale(LC_CTYPE, TEXT(""));
	SYSTEM_INFO sysInfo;
	GetSystemInfo(&sysInfo);

    //准备线程数量
	const int NThread = sysInfo.dwNumberOfProcessors + 2;

    //创建一个完成端口
	HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, NThread);
	HANDLE * thread_handles = new HANDLE[NThread];

    //创建线程
	for (int i = 0; i < NThread; ++i)
		thread_handles[i] = (HANDLE)_beginthreadex(0, 0, io_thread, (void*)iocp, 0, 0);

	HANDLE hFile = CreateFile(
		TEXT("D:/download/bookmarks.html"),  //这里自己修改                                       
		GENERIC_READ,                                  
		FILE_SHARE_READ,                              
		NULL,                                         
		OPEN_EXISTING,                                
		FILE_FLAG_OVERLAPPED ,  //注意.
		NULL                                          
		);
	DWORD numread = 0 , ret = 0;
	
    //初始化数据 , 这些数据将在完成后被传递到线程中
	cp_overlapped * s = new cp_overlapped; 
	s->overlap.hEvent = NULL;
	s->overlap.Offset = 0;
	s->overlap.OffsetHigh = 0;
    
    //初始化数据 , 这些数据将在完成后被传递到线程中
	cp_key * key = new cp_key;
	key->hFile = hFile;

    //将文件handle 与 完成端口 关联在一起 , 注意 key 的类型转换
	CreateIoCompletionPort(hFile, iocp,(DWORD)key, 0);
    
    //读取文件 , 等读完后, 将有一个线程处理读完后的步骤
    //注意 cp_overlapped 的使用, 在读完后, 其中一个线程将获取此数据
	ret = ReadFile(hFile, s->buf, 1024, &numread,&s->overlap);
	printf("readfile ret:%d , ERR:%d\n", ret, GetLastError());
	

    //等待一下
	for (int i = 0; i < 2; ++i)
		Sleep(1000);

    //告诉所有线程去死吧
	for (int i = 0; i < NThread; ++i)
		PostQueuedCompletionStatus(iocp, 0, (DWORD)0, 0);
    
	WaitForMultipleObjects(NThread, thread_handles, TRUE, -1);

	return 0;
}

 

相关标签: 完成端口