首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >IAsyncResult对ThreadPool

IAsyncResult对ThreadPool
EN

Stack Overflow用户
提问于 2014-01-22 09:14:13
回答 3查看 5.6K关注 0票数 13

我最近刚遇到IAsyncResult,玩了很长一段时间。我真正想知道的是,当我们有更好的替代方法时,为什么要使用IAsyncResult呢?根据我目前对两者的理解,我会选择在几乎每一种情况下都使用ThreadPool。因此,我的问题是,是否有IAsyncResult比其他上下文更受欢迎?

为什么我不喜欢IAsyncResult:

  • 增加了BeginXXX和EndXXX的复杂性
  • 如果调用者不关心返回值,他可能会忘记调用EndXXX
  • API设计中的冗余增加(我们需要为我们希望异步运行的每个方法创建开始和结束包装方法)
  • 降低可读性

要将其放入代码中:

ThreadPool

代码语言:javascript
复制
  public void ThreadPoolApproach()
  {
     ThreadPool.QueueUserWorkItem( ( a ) =>
     {
        WebClient wc = new WebClient();
        var response = wc.DownloadString( "http://www.test.com" );
        Console.WriteLine( response );
     } );
  }

IAsyncResult

代码语言:javascript
复制
  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" );
  }
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 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语言中对异步/等待关键字的支持作了进一步扩展。

票数 17
EN

Stack Overflow用户

发布于 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绑定的工作

代码语言:javascript
复制
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更受青睐.

票数 6
EN

Stack Overflow用户

发布于 2014-01-26 14:26:20

基本上,这两种方式只是同一件事的不同行为。使用IAsyncResult over ThreadPool的原因之一是返回值: Threading.WaitCallback返回void,因此您不能通过ThreadPool.QueueUserWorkItem调用直接返回任何值,但在IAsyncResult方法中可以返回。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21278602

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档