我正在玩SocketAsyncEventArgs
和IO完成端口。
我一直在寻找,但我似乎无法找到.NET如何处理竞争条件。
需要澄清这个堆栈溢出问题: https://stackoverflow.com/a/28690948/855421
作为旁注,请不要忘记您的请求可能已同步完成。也许您在while循环中从TCP流中读取,一次512字节。 如果套接字缓冲区中有足够的数据,多个ReadAsyncs可以立即返回而不进行任何线程切换。 [强调我的]
为了简单起见。让我们假设一个客户端是一台服务器。服务器正在使用IOCP。如果客户端是快速编写器但服务器是一个慢速读取器,IOCP是否意味着内核/底层进程可以发出多个线程的信号?
1 So, socket reads 512 bytes, kernel signals a IOCP thread 2 Server processes new bytes 3 socket receives another X bytes but server is still processing previous buffer
内核是否会启动另一个线程? SocketAsyncEventArgs
有一个Buffer
,根据定义是:"获取与异步套接字方法一起使用的数据缓冲区。"因此,如果我理解正确的话,缓冲区不应该在SocketAsyncEventArgs
的生命周期内发生变化。
什么阻止SocketAsyncEventArgs.Buffer
被IOCP线程2破坏?
或者.NET框架是否同步IOCP线程?如果是这样,那么如果IOCP线程1阻止先前的读取,那么启动新线程的重点是什么?
答案 0 :(得分:1)
我一直在寻找,但我似乎无法找到.NET如何处理竞争条件。
在大多数情况下,它没有。这取决于你做到这一点。但是,你的问题并不清楚你确实存在竞争条件问题。
中询问此文字如果套接字缓冲区中有足够的数据,多个ReadAsyncs可以立即返回而不进行任何线程切换
首先,要明确:方法的名称为ReceiveAsync()
,而不是ReadAsync()
。其他类,例如StreamReader
和NetworkStream
都有ReadAsync()
方法,这些方法与您的问题几乎没有关系。现在,澄清......
该引用是关于竞争条件的相反。该文本的作者警告您,如果您恰好在已经准备好读取数据的套接字上调用ReceiveAsync()
,则将同步读取数据并且SocketAsyncEventArgs.Completed
事件将不以后提出。调用ReceiveAsync()
的线程也负责处理已读取的数据。
所有这一切都会发生在一个线程中。在那种情况下,不会有任何竞争条件。
现在,让我们考虑一下你的快速作家,慢读者和#34;场景。可能发生的最糟糕的情况是,第一次读取可以在任何线程中发生,但不会立即完成,但是当Completed
事件被提升时,作者已经超越读者的节奏。在这种情况下,由于处理Completed
事件的部分可能再次调用ReceiveAsync()
,现在将同步返回,因此IOCP线程池线程将在调用{{1 }}。不需要新的线程,因为当前的IOCP线程正在同步完成所有工作。但它 阻止该线程处理其他 IOCP事件。
所有这些意味着,如果你有一些服务器正在处理的其他套接字,并且还需要调用ReceiveAsync()
,那么框架必须确保那里有'可用于处理I / O的IOCP线程池中的另一个线程。但是,这是一个完全不同的套接字,无论如何你都必须为该套接字使用完全不同的缓冲区。
再次,没有竞争条件。
现在,所有这一切,如果你想让真的混淆, 可以在.NET ReceiveAsync()
API中使用异步I / O(无论是否Socket
或BeginReceive()
或甚至将套接字包裹在ReceiveAsync()
并使用NetworkStream
),使您做具有竞争条件一个特定的插座。
我甚至不愿意提及它,因为在你的问题中没有任何证据证明这与你有关,也没有你甚至真的对这个细节水平感兴趣。添加此解释可能会让事情变得混乱。但是,为了完整起见......
在任何给定时间都可以在套接字上发出多个读操作。这有点类似于双缓冲或三缓冲视频显示(如果您熟悉该概念)。我们的想法是,在新数据进入时您可能仍在处理读取操作,并且在您处理当前读取操作之前,处理该数据的新读取操作将更加高效。
这听起来不错,但实际上由于Windows调度线程的方式,特别是不保证线程调度的特定顺序,如果您尝试以这种方式实现代码,则可能会创建您的代码将看到无序完成的读取操作。也就是说,如果你连续两次调用ReadAsync()
(当然有两个不同的ReceiveAsync()
对象和两个不同的缓冲区),你的SocketAsyncEventArgs
事件处理程序可能会被调用先缓冲。
这不是因为读取操作本身完全无序。他们没有。因此,强调上面的"你的" 。问题是,当处理IO完成的IOCP线程以正确的顺序运行时(因为缓冲区按照您通过多次调用Completed
提供的顺序填充),第二个IOCP线程将变为可运行的风成为实际安排由Windows运行的第一个线程。
这不难对付。您只需确保在发出读取操作时跟踪缓冲区序列,以便稍后可以按正确顺序重新组合缓冲区。所有可用的异步选项都提供了一种机制,您可以包含其他用户状态数据(例如ReceiveAsync()
),这样您就可以使用它来跟踪缓冲区的顺序。
同样,这并不常见。对于大多数情况,完全有序的实现(仅在您完成当前读取操作后才发出新的读取操作)就足够了。如果您一直担心多缓冲区读取实现是否正确,请不要打扰。坚持简单的方法。