【问题标题】:How to ensure thread safe in iocp receive?如何确保 iocp 接收中的线程安全?
【发布时间】: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

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

【问题讨论】:

    标签: multithreading winapi winsock iocp


    【解决方案1】:

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

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

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

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

    【讨论】:

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