我最近刚遇到IAsyncResult,玩了很长一段时间。我真正想知道的是,当我们有更好的替代方法时,为什么要使用IAsyncResult呢?根据我目前对两者的理解,我会选择在几乎每一种情况下都使用ThreadPool。因此,我的问题是,是否有IAsyncResult比其他上下文更受欢迎?
为什么我不喜欢IAsyncResult:
要将其放入代码中:
ThreadPool
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" );
}发布于 2014-01-26 15:03:46
不,你的两个代码片段之间有很大的区别。实际上,两者都使用线程池,当然,第一个线程池显式地使用线程池。第二种方法是在一个线程池线程上执行IAsyncResult回调,这种方式很难看到(并且破坏了)。
线程池是一个共享资源,在一个大型程序中,TP线程将有很多用途。不只是在您自己的代码中显式地使用,.NET框架也使用它们。对于在线程池上运行的代码类型的指导是,它是快速执行的代码,并且不会发出任何阻塞调用,从而使TP线程处于等待状态。阻塞是以一种非常低效率的方式使用非常昂贵的操作资源,并大量使用其他可能使用TP线程的代码。线程池的一个重要部分是调度程序,它试图将执行TP线程的数量限制在机器可用的CPU核心数量上。
但是,在第一个片段中,阻塞正是您所要做的。WebClient.DownloadString()是一种非常慢的方法,它不能比您的Internet连接更快地完成,或者连接另一端的服务器将允许这样做。实际上,您占用TP线程的时间可能是几分钟。没有做太多的工作,它一直在等待Socket.Read()调用的完成。有效的CPU核心利用率最多只有几%。
当您使用()或XxxxAsync()方法时,这与BeginXxxx()或XxxxAsync()方法有很大的不同。它在内部实现为一些代码,以要求操作系统启动I/O操作。只需要几微秒。在DownloadStringAsync()的情况下,操作系统将请求传递给设备驱动程序TCP/IP堆栈。其中,它将作为I/O请求队列中的数据项。你的电话很快就回来了。
最终,网卡从服务器获得数据,驱动程序完成I/O请求。通过几个层,这将使CLR获取另一个TP线程并运行回调。不管你对数据做什么,你都可以快速完成,这是一种通常需要微秒的处理步骤。
注意不同之处,您的第一段代码占用TP线程数分钟,异步版本将线程绑定数微秒。异步版本的扩展性要好得多,能够处理许多I/O请求。
异步版本代码的一个重要问题是,要正确编写代码要困难得多。同步版本中的局部变量需要成为异步版本中类的字段。调试也要困难得多。这就是为什么.NET获得了Task类,并在C#和VB.NET语言中对异步/等待关键字的支持作了进一步扩展。
发布于 2014-01-29 00:24:59
让我们抛开自然异步的IO绑定操作,这些操作不需要专门的线程来完成(请参阅Stephen的没有线程 )。在池线程上执行自然异步DownloadString API的同步版本DownloadStringAsync没有多大意义,因为阻塞一个宝贵的资源是徒劳的:线程。
相反,让我们专注于CPU绑定的计算操作,这确实需要一个专门的线程。
首先,在AsyncResult<T>框架中没有标准的类,我相信,您在代码中引用的AsyncResult<string>的实现是从Jeffrey的并发事务: CLR异步编程模型的实现文章中获取的。我还相信,作者展示了如何为教育目的实现AsyncResult<T>,并举例说明了CLR实现的样子。他通过ThreadPool.QueueUserWorkItem在池线程上执行一段工作,并实现IAsyncResult来提供完成通知。更多细节可以在LongTask.cs中找到,并附在本文中。
因此,要回答这个问题:
我真正想知道的是,当我们有更好的替代方法时,为什么要使用IAsyncResult呢?
ThreadPool**“--这不是"**IAsyncResult** vs ”的情况,相反,在您的问题的上下文中,IAsyncResult** 是对E 230的补充,它提供了一种通知调用者工作项已完成执行的方法。ThreadPool.QueueUserWorkItem API本身不具有此特性,它只返回指示工作项是否已在池线程上成功排队以进行异步执行的bool。
但是,对于这个场景,您根本不需要实现或使用ThreadPool.QueueUserWorkItem。框架允许在ThreadPool上异步执行委托并跟踪完成状态,只需使用委托的BeginInvoke方法即可。这就是框架为委托实现异步编程模型模式的方式。例如,下面是如何使用BeginInvoke执行一些与CPU绑定的工作
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();
}最后,值得一提的是,APM模式正在被一个更加方便和结构良好的基于任务的异步模式(TAP)所取代。正是推荐,TAP模式应该比其他更低级别的API更受青睐.
发布于 2014-01-26 14:26:20
基本上,这两种方式只是同一件事的不同行为。使用IAsyncResult over ThreadPool的原因之一是返回值: Threading.WaitCallback返回void,因此您不能通过ThreadPool.QueueUserWorkItem调用直接返回任何值,但在IAsyncResult方法中可以返回。
https://stackoverflow.com/questions/21278602
复制相似问题