C# IAsyncResult vs ThreadPool

C# IAsyncResult vs ThreadPool,c#,threadpool,iasyncresult,C#,Threadpool,Iasyncresult,我最近刚刚遇到了IAsyncResult,并且已经玩了很长时间了。我真正想知道的是,当我们有更好的替代线程池时,为什么要使用IAsyncResult?根据我目前对两者的理解,我会选择在几乎所有情况下使用ThreadPool。所以我的问题是,是否有任何情况下iSyncResult比另一个更受欢迎 我不喜欢IAsyncResult的原因: 增加了BeginXXX和EndXXX的复杂性 如果调用方不关心返回值,则可能会忘记调用EndXXX 增加了API设计中的冗余(我们需要创建Begin和End包装

我最近刚刚遇到了IAsyncResult,并且已经玩了很长时间了。我真正想知道的是,当我们有更好的替代线程池时,为什么要使用IAsyncResult?根据我目前对两者的理解,我会选择在几乎所有情况下使用ThreadPool。所以我的问题是,是否有任何情况下iSyncResult比另一个更受欢迎

我不喜欢IAsyncResult的原因:

  • 增加了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();
}