【发布时间】:2014-07-27 02:45:47
【问题描述】:
为了这个问题,我做了一个超级简化的例子:
using System;
using System.Net;
using System.Net.Sockets;
namespace Loop
{
class Program
{
public static void Main (string[] args)
{
TcpListener server = new TcpListener(IPAddress.Any, 1337);
server.Start();
Console.WriteLine("Starting listener on {0}:{1}", IPAddress.Any, 1337);
while (true)
{
if (server.Pending())
{
Console.WriteLine("Activity...");
Socket client = server.AcceptSocket();
IPEndPoint clientAddress = (IPEndPoint) client.RemoteEndPoint;
Console.WriteLine("Accepted client: {0}:{1}", clientAddress.Address, clientAddress.Port);
client.Close();
Console.WriteLine("Closed connection to: {0}:{1}", clientAddress.Address, clientAddress.Port);
}
else
{
// Currently takes 100% of my CPU (well, actually Core - 25%, but you get the idea).
// How do I idle (CPU @ 0%) the loop until pending connection?
}
}
}
}
}
评论已经包含了问题,但是是的,我如何让循环空闲直到有一个实际的挂起连接,这样我的 CPU 才不会被融化?
当给定的套接字上有挂起的连接时,有没有办法只在操作系统事件上监听和唤醒? (像libuv (node.js) 这样的东西,这就是我添加single-threaded 的原因)
我的实际实现是针对一个相当基本的 Reactor 事件循环,但是是的,我不知道如何使用 C# 监听 OS 事件(以及是否有可能)。
我知道 BeginAccept 和其他 Async 家族,但由于他们的多线程性质,这些人是不可接受的。
另外,我知道我可以在循环中简单地 Thread.Sleep,但我正在寻找基于事件的行为。
附:我正在使用 Mono,目标是 Linux 可执行文件。
【问题讨论】:
-
如果要使用阻塞样式,可以先调用AcceptSocket而不用等待Pending;在接受连接之前,呼叫将阻塞(不旋转)。或者,您可以在轮询循环中添加睡眠以减少 CPU 开销,但在编写网络 I/O 代码以避免轮询并使用适当的同步工具仅在 I/O 可用时唤醒时会更好。跨度>
-
我只想指出
BeginAccept不一定是多线程的(至少就您的代码而言)。如果你提供一个同步上下文,如果你可以访问await/async,AcceptSocketAsync非常容易使用,它会在使用与同步非常相似的代码时异步处理事情,并完全解决多线程问题(因为您实际上在代码中一直在处理一个线程)。 Mono 现在应该支持await/async。 -
@Luaan,使用多线程,我认为每个请求都会产生额外的线程。
-
@jolt 好吧,
BeginAccept没有,不是真的。它从ThreadPool借用一个线程,只是在极短的时间内(将实际回调放入调用队列)。如果您调用BeginAccept一百次,它不会产生 100 个线程。它只是注册了一百个不同的异步套接字。这就是 IOCP 的美妙之处——您无需使用 CPU 线程来处理 I/O 密集型工作。要处理与一千个客户端的通信,您不再需要一千个线程(与“同步”模型不同)。它实际上很酷:) -
我很好奇:您从哪里得到使用 Pending 的想法?我经常看到这个错误。
标签: single-threaded c# events mono tcplistener single-threaded