C# IAsyncResult vs ThreadPool
我最近刚刚遇到了IAsyncResult,并且已经玩了很长时间了。我真正想知道的是,当我们有更好的替代线程池时,为什么要使用IAsyncResult?根据我目前对两者的理解,我会选择在几乎所有情况下使用ThreadPool。所以我的问题是,是否有任何情况下iSyncResult比另一个更受欢迎 我不喜欢IAsyncResult的原因:C# IAsyncResult vs ThreadPool,c#,threadpool,iasyncresult,C#,Threadpool,Iasyncresult,我最近刚刚遇到了IAsyncResult,并且已经玩了很长时间了。我真正想知道的是,当我们有更好的替代线程池时,为什么要使用IAsyncResult?根据我目前对两者的理解,我会选择在几乎所有情况下使用ThreadPool。所以我的问题是,是否有任何情况下iSyncResult比另一个更受欢迎 我不喜欢IAsyncResult的原因: 增加了BeginXXX和EndXXX的复杂性 如果调用方不关心返回值,则可能会忘记调用EndXXX 增加了API设计中的冗余(我们需要创建Begin和End包装
- 增加了BeginXXX和EndXXX的复杂性
- 如果调用方不关心返回值,则可能会忘记调用EndXXX
- 增加了API设计中的冗余(我们需要创建Begin和End包装器方法 对于我们希望异步运行的每个方法)
- 可读性降低
public void ThreadPoolApproach()
{
ThreadPool.QueueUserWorkItem( ( a ) =>
{
WebClient wc = new WebClient();
var response = wc.DownloadString( "http://www.test.com" );
Console.WriteLine( response );
} );
}
IAsyncResult
public void IAsyncResultApproach()
{
var a = BeginReadFromWeb( ( result ) =>
{
var response = EndReadFromWeb( result );
Console.WriteLine( response );
}, "http://www.test.com" );
}
public IAsyncResult BeginReadFromWeb( AsyncCallback a, string url )
{
var result = new AsyncResult<string>( a, null, this, "ReadFromFile" );
ThreadPool.QueueUserWorkItem( ( b ) =>
{
WebClient wc = new WebClient();
result.SetResult( wc.DownloadString( url ) );
result.Complete( null );
} );
return result;
}
public string EndReadFromWeb( IAsyncResult result )
{
return AsyncResult<string>.End( result, this, "ReadFromFile" );
}
public void IAsyncResultApproach()
{
var a=从Web开始((结果)=>
{
var响应=EndReadFromWeb(结果);
控制台写入线(响应);
}, "http://www.test.com" );
}
公共IAsyncResult BeginReadFromWeb(异步回调,字符串url)
{
var result=new AsyncResult(a,null,这是“ReadFromFile”);
ThreadPool.QueueUserWorkItem((b)=>
{
WebClient wc=新的WebClient();
SetResult(wc.DownloadString(url));
结果:完整(空);
} );
返回结果;
}
公共字符串EndReadFromWeb(IAsyncResult结果)
{
返回AsyncResult.End(结果,此为“ReadFromFile”);
}
基本上,这两种方式只是同一事物的不同行为。对线程池使用IAsyncResult的原因之一是返回值:Threading.WaitCallback返回void,因此不能通过ThreadPool.QueueUserWorkItem调用直接返回任何值,但可以使用IAsyncResult方法 不,您的两个代码片段之间存在巨大差异。两者实际上都使用线程池,第一个当然是显式地使用线程池。第二种方法是以一种不太明显(且不完整)的方式执行,即在线程池线程上执行IAsyncResult回调
线程池是一种共享资源,在大型程序中,TP线程有很多用途。不仅在您自己的代码中显式地使用它们,.NET Framework也使用它们。对于在线程池上运行的代码类型的指导是,它应该是快速执行的代码,并且不会进行任何阻塞调用,从而使TP线程进入等待状态。阻塞是以一种非常低效的方式使用非常昂贵的操作资源,并使可能使用TP线程的其他代码变得混乱。线程池的一个重要部分是调度程序,它试图将执行TP线程的数量限制为机器可用的CPU内核数量
但在第一个代码段中,阻塞正是您要做的。WebClient.DownloadString()是一种速度非常慢的方法,其完成速度不能超过Internet连接或连接另一端的服务器所允许的速度。实际上,您占用一个TP线程的时间可能为分钟。它根本不做任何工作,而是不断地等待Socket.Read()调用完成。CPU核心的有效利用率最多只有几个百分点
使用BeginXxxx()或XxxxAsync()方法时,这与使用BeginXxxx()或XxxxAsync()方法时大不相同。它在内部实现为一段代码,要求操作系统启动I/O操作。只需要几微秒。操作系统将请求传递给设备驱动程序,在DownloadStringAsync()的情况下是TCP/IP堆栈。它将作为数据项位于I/O请求队列中的位置。你的电话很快就回了
最后,网卡从服务器获取数据,驱动程序完成I/O请求。通过几个层,CLR可以抓取另一个TP线程并运行回调。您可以快速地处理数据,这是一种通常需要几微秒的处理步骤
请注意区别,您的第一个代码占用TP线程数分钟,异步版本占用线程数微秒。异步版本可以更好地扩展,能够处理许多I/O请求
异步版本的代码的一个重要问题是,正确编写代码要困难得多。同步版本中的局部变量需要成为异步版本中类的字段。调试也要困难得多。这就是.NET获得Task类的原因,该类在以后的C#和VB.NET语言中支持async/await关键字,进一步扩展。让我们把自然异步IO绑定的操作放在一边,这些操作不需要专用线程来完成(请参见Stephen Cleary)。在池线程上执行自然异步API的同步版本
DownloadString
,没有多大意义,因为您徒劳地阻塞了宝贵的资源:线程
相反,让我们专注于CPU限制的计算操作,这确实需要一个专用线程
首先,在.NET框架中没有标准的AsyncResult
类。我相信,您在代码中引用的AsyncResult
的实现取自Jeffrey Richter的文章。我还认为作者展示了如何实现AsyncResult
,以达到教育目的,并举例说明了CLR的实现可能是什么样子。他通过ThreadPool.QueueUserWorkItem
在池线程上执行一项工作,并实现IAsyncResult
以提供完成通知。更多详细信息,请参阅文章的附件
因此,要回答这个问题:
我真正想知道的是,当我们有办法的时候,为什么要使用IAsyncResult
有更好的替代线程池吗
这不是一个“IAsyncResult
vs
static void Main(string[] args)
{
Console.WriteLine("Enter Main, thread #" + Thread.CurrentThread.ManagedThreadId);
// delegate to be executed on a pool thread
Func<int> doWork = () =>
{
Console.WriteLine("Enter doWork, thread #" + Thread.CurrentThread.ManagedThreadId);
// simulate CPU-bound work
Thread.Sleep(2000);
Console.WriteLine("Exit doWork");
return 42;
};
// delegate to be called when doWork finished
AsyncCallback onWorkDone = (ar) =>
{
Console.WriteLine("enter onWorkDone, thread #" + Thread.CurrentThread.ManagedThreadId);
};
// execute doWork asynchronously on a pool thread
IAsyncResult asyncResult = doWork.BeginInvoke(onWorkDone, null);
// optional: blocking wait for asyncResult.AsyncWaitHandle
Console.WriteLine("Before AsyncWaitHandle.WaitOne, thread #" + Thread.CurrentThread.ManagedThreadId);
asyncResult.AsyncWaitHandle.WaitOne();
// get the result of doWork
var result = doWork.EndInvoke(asyncResult);
Console.WriteLine("Result: " + result.ToString());
// onWorkDone AsyncCallback will be called here on a pool thread, asynchronously
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}