0%

使用IOCP实现简易的线程池

前言

Windows I/O完成端口(IOCP)是一个强大的异步I/O模型,不仅可以用于网络编程,还可以用来实现高效的线程池。本文将介绍如何使用IOCP实现一个简单但实用的线程池。

IOCP简介

IOCP(I/O Completion Port)是Windows提供的一个用于处理异步I/O的可扩展模型。它可以:

  • 高效管理线程池
  • 实现负载均衡
  • 最小化线程上下文切换

实现思路

  1. 创建完成端口
  2. 创建工作线程池
  3. 投递任务
  4. 处理完成回调

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class IOCPThreadPool {
private:
HANDLE m_hIOCP;
std::vector<std::thread> m_threads;
bool m_running;

public:
IOCPThreadPool(size_t threadCount = 0) {
m_running = true;
m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

// 如果未指定线程数,使用处理器核心数
if (threadCount == 0) {
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
threadCount = sysInfo.dwNumberOfProcessors;
}

// 创建工作线程
for (size_t i = 0; i < threadCount; i++) {
m_threads.emplace_back(&IOCPThreadPool::WorkerThread, this);
}
}

template<typename F>
void EnqueueTask(F&& task) {
auto taskPtr = new std::function<void()>(std::forward<F>(task));
PostQueuedCompletionStatus(m_hIOCP, 0, (ULONG_PTR)taskPtr, nullptr);
}

private:
void WorkerThread() {
while (m_running) {
DWORD bytesTransferred;
ULONG_PTR completionKey;
LPOVERLAPPED overlapped;

// 等待任务
BOOL result = GetQueuedCompletionStatus(
m_hIOCP,
&bytesTransferred,
&completionKey,
&overlapped,
INFINITE
);

if (result && completionKey) {
auto task = reinterpret_cast<std::function<void()>*>(completionKey);
(*task)();
delete task;
}
}
}
};

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
IOCPThreadPool pool;

// 投递任务
pool.EnqueueTask([]() {
std::cout << "Task 1 executed in thread: "
<< std::this_thread::get_id() << std::endl;
});

pool.EnqueueTask([]() {
std::cout << "Task 2 executed in thread: "
<< std::this_thread::get_id() << std::endl;
});

std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}

优点分析

  1. 高效的线程管理

    • IOCP自动实现线程池的负载均衡
    • 最小化线程上下文切换
  2. 简单易用

    • 支持lambda表达式
    • 支持任意可调用对象
  3. 性能优势

    • 由系统内核管理线程调度
    • 适合高并发场景

注意事项

  1. 内存管理

    • 注意任务对象的生命周期
    • 避免内存泄漏
  2. 异常处理

    • 工作线程中需要捕获异常
    • 避免线程意外退出

总结

使用IOCP实现线程池不仅可以获得良好的性能,还能充分利用Windows系统的特性。这个简单的实现可以作为更复杂线程池的基础,根据实际需求进行扩展。


参考资料:

  1. Windows系统编程指南
  2. MSDN - I/O Completion Ports
  3. C++并发编程实战