2012-12-11 67 views
3

我想知道如何实现我自己的异步方法的最佳方式。我已阅读this article,它说,将同步方法封装到任务中并将其用作异步方法并不是一个好主意,因为这种方式使用其他线程并使用更多资源,这就是我们想要避免使用异步的方法。如何以正确的方式实现我的异步方法?

在文章中使用TaskCompletionSource,但我不知道如何使用它。 TaskCompletionSource的目的是什么?

回答

4

编写async方法的最佳资源是Task-based Asynchronous Pattern (TAP) document by Stephen Toub。该文件中的大部分信息现在是part of the official MSDN documentation

如果你想要一个较短的(但仍然是彻底的)教程,我推荐自己的async/await intro blog post,还有其他几个教程。一旦你了解了一下async,下一个好资源是official async/await FAQ

Channel9上还有很多很棒的async视频。当它仍然是CTP时,它们中的很多(大多数?)讨论了async,但是当它投入生产时很少进行设计更改。

要回答您的具体问题,请使用TaskCompletionSource为现有异步操作(例如,I/O操作,计时器或事件)创建一个Task包装。您不能执行由TaskCompletionSource创建的Task中的代码;您应该使用Task.RunTask中执行代码。

+0

但是如果我使用task.Run创建一个新线程,并且如果我没有错,这将创建一个新线程,这是我们想要避免的原因,因为使用更多资源并且可扩展性更差。是否有可能在不创建新线程的情况下在任务中执行代码? –

+0

代码将在哪里运行? –

+0

是的。然后,如果我使用像killercam这样的代码,那就是我是doinf,它与在任务中包装同步方法以创建异步方法的方法相同。如果这是真的,在我的文章中,作者说这不是一个好习惯。 –

2

在另一个async方法(使用.NET 5.0 async关键字)内调用方法很好。我不确定我完全理解你的问题,但请看下面的简单例子。要出发int与数之间显示的质数,我们可以写

async void DisplayPrimes() 
{ 
    int result = await GetPrimesCountAsync(2, 10000); 
    Console.WriteLine(result); 
} 

其中

Task<int> GetPrimesCountAsync(int start, int count) 
{ 
    return Task.Run(() => 
     ParrallelEnumerable.Range(start, count).COunt(n => 
      Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0))); 
} 

这里async修饰符告诉编译器把等待作为关键字,而不是一个标识符应任何含糊之处在该方法内出现。这确保了在C#5.0之前编写的可能使用await作为标识符的代码仍然无错地编译。异步修饰符只能应用于返回void或Task/Task<TResult>的方法和lambda表达式。

在遇到await表达式时,执行(通常)返回给调用者 - 而非像在使用yeild return一样。但是在返回之前,运行时会为等待的任务附加一个延续,以确保当任务完成时,执行会跳回到继续停止的方法中。我想你可以写上面的方法,显示编译器将其转换为什么

void DisplayPrimesCount() 
{ 
    var awaiter = GetPrimesCountAsync(2, 100000).GetAwaiter(); 
    awaiter.OnCompleted(() => 
    { 
     int result = awaiter.GetResult(); 
     Console.WriteLine(result); 
    }); 
} 

我希望这有助于。


编辑。这样可以避免任务阻塞(这是很难说你的情况是什么,你已经张贴任何代码),你需要把你想创建“一个级别”

private async Task<bool> TestAsync() 
{ 
    return await Task.Run(() => 
    { 
     // Do stuff non-blocking.    
     return true; 
    }).ConfigureAwait(continueOnCapturedContext:false); 
} 

TestAsync这里会把async过程能够通过在线程池线程而不是UI上下文上执行它的“return”语句来完成。这将立即返回给呼叫者,但请注意,但是,如果您继续使用Task.Result,则任何异常都会被包装在AggregateException中,您必须相应地处理这些异常。

可以找到一个很好的解释on MSDN here。在进入async/await之前,我会推荐学习C#4.0任务,因为它会让您更好地理解并发性。

+1

我的理解正确的文章,问题是,如果我使用Tas.Run,​​我创建一个新的线程,并且这使用更多的资源,所以目标是不使用异步方法的新线程。问题是,到目前为止,我不知道如何在没有任务的情况下实现异步方法。运行而不是阻止用户界面。本文使用TaskCompletionSource,它不创建一个新线程,但我不知道如何以正确的方式使用它。 –

+0

请参阅编辑一些更多的说明。 – MoonKnight

+1

听起来像是你误解了运行“异步”的概念,因为它基本上总是涉及第二个线程。如果你不习惯创建/删除额外的线程,你可以创建一个额外的线程,等待事情处理(不忙循环!),基本上重用它的资源。缺点是,每次运行只能处理一个异步请求,但它会起作用。 – Mario