C# IOCP线程-澄清?
在阅读以下内容后: 设备完成其工作后(IO操作)-它通知CPU 通过中断 但是,该“完成”状态仅适用于 存在于操作系统级别;进程有自己的内存空间,必须 被通知 因为库/BCL使用的是标准的p/Invoke 重叠的I/O系统,它已将句柄注册到 I/O完成端口(IOCP),它是线程池的一部分 因此,一个I/O线程池线程被短暂借用来执行APC, 通知任务已完成 我对粗体部分很感兴趣: 如果我理解正确,IO操作完成后,它必须通知执行IO操作的实际进程 问题1: 这是否意味着它会为每个完成的IO操作获取一个新的线程池线程?或者,这是一个专门的线程数 问题2: 看看:C# IOCP线程-澄清?,c#,async-await,iocp,C#,Async Await,Iocp,在阅读以下内容后: 设备完成其工作后(IO操作)-它通知CPU 通过中断 但是,该“完成”状态仅适用于 存在于操作系统级别;进程有自己的内存空间,必须 被通知 因为库/BCL使用的是标准的p/Invoke 重叠的I/O系统,它已将句柄注册到 I/O完成端口(IOCP),它是线程池的一部分 因此,一个I/O线程池线程被短暂借用来执行APC, 通知任务已完成 我对粗体部分很感兴趣: 如果我理解正确,IO操作完成后,它必须通知执行IO操作的实际进程 问题1: 这是否意味着它会为每个完成的IO操作
for (int i=0;i<1000;i++)
{
PingAsync_NOT_AWAITED(i); //notice not awaited !
}
for(int i=0;i这有点宽泛,所以让我来谈谈要点:
可以说,IOCP线程位于一个单独的线程池中—这是I/O线程设置。因此它们不会与用户线程池线程冲突(就像您在正常的wait
操作或ThreadPool.QueueWorkerItem
中使用的线程)
就像普通的线程池一样,它只会随着时间的推移缓慢地分配新线程。因此,即使异步响应的峰值同时发生,您也不会有1000个I/O线程
在一个完全异步的应用程序中,你不会像工作线程那样拥有超过内核数量的内核,不管是给予还是索取。这是因为你要么在做大量的CPU工作,你应该把它发布到一个普通的工作线程上,要么在做I/O工作,你应该作为一个异步操作来做
这个想法是你在I/O回调中花费很少的时间-你不会阻塞,也不会做很多CPU工作。如果你违反了这一点(比如,在回调中添加Thread.Sleep(10000)
),那么,.NET会随着时间的推移创建成吨的IO线程-但这只是不正确的使用
现在,I/O线程与普通CPU线程有何不同?它们几乎相同,只是等待不同的信号-两者(简化警报)都只是一个循环,而循环通过一个方法,该方法在应用程序(或操作系统)的其他部分对新工作项排队时提供控制。主要区别在于I/O线程使用IOCP队列(OS管理),而普通工作线程有自己的队列,完全由.NET管理并可由应用程序程序员访问
请注意,不要忘记您的请求可能已同步完成。可能您正在while循环中读取TCP流,每次读取512字节。如果套接字缓冲区中有足够的数据,则多个ReadAsync
s可以立即返回,而无需进行任何线程切换。这通常不是问题,因为I/O通常是典型应用程序中时间最密集的工作,因此不必等待I/O通常是可以的。但是,依赖于异步发生的某些部分的错误代码(即使这不能保证)很容易破坏应用程序
这是否意味着它会为每个线程获取一个新的线程池线程
已完成的IO操作?还是专用的线程数
这个
为每一个I/O请求创建一个新线程会非常低效,甚至达不到目的。相反,运行时从少量线程开始(确切数量取决于您的环境),并根据需要添加和删除工作线程(具体的算法也因您的环境而异。).NET的任何主要版本在此实现中都发生了变化,但基本思想保持不变:运行时尽最大努力创建和维护有效服务所有I/O所需的线程。在我的系统上(Windows 8.1、.NET 4.5.2)一个全新的控制台应用程序在进入Main
的过程中只有3个线程,在请求实际工作之前,这个数字不会增加
这是否意味着我将同时拥有1000个IOCP线程池线程
(有点)在这里跑步,什么时候全部完成
否。当您发出I/O请求时,线程将在完成端口上等待获取结果,并调用注册的任何回调来处理结果(通过BeginXXX
方法或作为任务的继续)。如果您使用任务而不等待它,则该任务将在此结束,线程将返回到线程池
如果您确实在等待它呢?1000个I/O请求的结果不会真的同时到达,因为中断不会同时到达,但假设间隔比我们需要处理它们的时间短得多。在这种情况下,线程池将继续旋转线程来处理结果,直到达到最大值,即nd任何进一步的请求都将在完成端口上排队。根据您的配置,这些线程可能需要一些时间来启动
考虑以下(故意制造的)玩具程序:
static void Main(string[] args) {
printThreadCounts();
var buffer = new byte[1024];
const int requestCount = 30;
int pendingRequestCount = requestCount;
for (int i = 0; i != requestCount; ++i) {
var stream = new FileStream(
@"C:\Windows\win.ini",
FileMode.Open, FileAccess.Read, FileShare.ReadWrite,
buffer.Length, FileOptions.Asynchronous
);
stream.BeginRead(
buffer, 0, buffer.Length,
delegate {
Interlocked.Decrement(ref pendingRequestCount);
Thread.Sleep(Timeout.Infinite);
}, null
);
}
do {
printThreadCounts();
Thread.Sleep(1000);
} while (Thread.VolatileRead(ref pendingRequestCount) != 0);
Console.WriteLine(new String('=', 40));
printThreadCounts();
}
private static void printThreadCounts() {
int completionPortThreads, maxCompletionPortThreads;
int workerThreads, maxWorkerThreads;
ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine(
"Worker threads: {0}, Completion port threads: {1}, Total threads: {2}",
maxWorkerThreads - workerThreads,
maxCompletionPortThreads - completionPortThreads,
Process.GetCurrentProcess().Threads.Count
);
}
在我的系统(有8个逻辑处理器)上,输出如下(结果可能因您的系统而异):
工作线程:0,完成端口线程:0,总线程数:3
工作线程:0,完成端口线程:8,总线程数:12
工作线程:0,完成端口线程:9,总线程数:13
工作线程:0,完成端口线程:11,总线程数:15
工作线程:0,完成端口线程:13,总线程数:17
工作线程:0,完成端口线程:15,总线程数:19
工人t
stream.BeginRead(
buffer, 0, buffer.Length,
ar => {
stream.EndRead(ar);
Interlocked.Decrement(ref pendingRequestCount);
}, null
);
private static void Main(string[] args)
{
Task.Run(() =>
{
int count = 0;
while (count < 30)
{
int _;
int iocpThreads;
ThreadPool.GetAvailableThreads(out _, out iocpThreads);
Console.WriteLine("Current number of IOCP threads availiable: {0}", iocpThreads);
count++;
Thread.Sleep(10);
}
});
for (int i = 0; i < 30; i++)
{
GetUrl(@"http://www.ynet.co.il");
}
Console.ReadKey();
}
private static async Task<string> GetUrl(string url)
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}