如何确保 iocp 接收中的线程安全?

Posted

技术标签:

【中文标题】如何确保 iocp 接收中的线程安全?【英文标题】:How to ensure thread safe in iocp receive? 【发布时间】:2015-08-17 14:59:30 【问题描述】:
DWORD bytes;
ULONG_PTR key; ChatOverlappedData* ol;
if (!GetQueuedCompletionStatus(hComp_, &bytes, &key, (LPOVERLAPPED*)&ol, 0)) 
    return false;

int type = ol->getNetType();
if (type == net::kAction_Accept) 
    onAccept(ol, bytes, key);
 else if (type == net::kAction_Recv) 
    onRecv(ol, bytes, key);
 else if (type == net::kAction_Send) 

 
return true; 

考虑以下场景,

客户端alice向服务器发送了两个命令,由三个数据包p1p2p3组成。前两个包形成第一个命令c1,第三个包形成第二个命令c2。在onRecv函数中,服务器需要将数据包推送到某种命令缓冲区中,形成完整的命令。

但是假设有三个线程t1t2t3,每个线程从GetQueuedCompletionStatus获取一个数据包(p1,p2,p3),

由于 windows 是抢占式操作系统,线程 t2 可以在 t1t3 之前运行。结果命令缓冲区将为p2->p1->p3p2->p3->p1

如何保证将数据包推送到命令缓冲区的动作线程安全?

【问题讨论】:

【参考方案1】:

最简单的解决方案是在每个方向上每个套接字只尝试一个重叠的 I/O 请求。所以发布一个重叠的读取操作,当它完成时,当你完成第一个处理时再发布一个。

发布多个这样的操作非常复杂,因为即使完成会按顺序发布,处理完成的线程也可能会乱序执行,您必须进行一些痛苦而复杂的跟踪。

针对同一连接在同一方向发布多个重叠操作的好处非常非常小。几乎永远不足以证明额外的复杂性是合理的。对于处理大量连接的服务器,通常根本不值得这样做,因为额外的内存消耗(或使用较小的缓冲区大小)实际上会使性能变差。

IOCP 的主要好处是更有效地发现哪些连接需要工作,并将该工作高效地分配给线程池。这就是最大连接数为 800 个的服务器与可以处理 10,000 个连接的服务器之间的区别。

【讨论】:

您对“我如何使用异步 I/O?” 的回答是 “改用同步 I/O。这样更容易。”?哎呀,这真的很有帮助。 不,一点也不。 I/O 仍然是异步的。该操作已发布并稍后完成。 嗯?假设您要处理 1,000 个客户。您发布了 1,000 个异步接收操作。假设其中两个向您发送数据。其中两个操作已完成,完成后将分派给您池中的两个线程。你还想要什么?你拿它比什么? 1000 个线程被同步操作阻塞? @amaninlove 你肯定想限制你积累的工作量,如果它变得太大,就会限制接收。一个简单的规则是在前一个请求完成之前不要为客户端发送新请求。这很容易通过使用指示客户端请求是否已发送的标志来实现。如果该标志设置为 true,则不要分派线程来执行新请求。当处理现有请求的线程完成它时,它可以将来自同一连接的任何“下一个请求”排队。 @amaninlove 虽然你可以做到,但我从不限制传入连接并始终从中接收数据。如果积累了太多未处理的数据,我会断开该客户端以“惩罚”它超出服务器。如果没有收到响应,这将转移客户端限制发送数据的义务。对于许多协议,这是合适的。一般来说,我相信将尽可能多的工作转移到客户端,以便服务器尽可能地扩展。当然,这并不总是合适的。

以上是关于如何确保 iocp 接收中的线程安全?的主要内容,如果未能解决你的问题,请参考以下文章

如何确保Java线程安全?

如何确保线程安全?

如何确保线程安全?

如何安全地终止线程? (使用指针)C++

如何创建线程?如何保证线程安全?

java 此类表示线程安全堆栈并使用CAS指令来确保线程安全。