我读过的每篇博文都告诉你如何在C#中使用异步方法,但由于一些奇怪的原因,从未解释如何构建自己的异步方法来使用。所以我现在有这个代码消耗我的方法:
private async void button1_Click(object sender, EventArgs e)
{
var now = await CountToAsync(1000);
label1.Text = now.ToString();
}
我写的这个方法是CountToAsync
:
private Task<DateTime> CountToAsync(int num = 1000)
{
return Task.Factory.StartNew(() =>
{
for (int i = 0; i < num; i++)
{
Console.WriteLine("#{0}", i);
}
}).ContinueWith(x => DateTime.Now);
}
这是使用Task.Factory
,编写异步方法的最佳方法,还是应该用另一种方式编写?
答案 0 :(得分:205)
除非您需要这种复杂程度,否则我不建议使用StartNew
。
如果您的异步方法依赖于其他异步方法,最简单的方法是使用async
关键字:
private static async Task<DateTime> CountToAsync(int num = 10)
{
for (int i = 0; i < num; i++)
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
return DateTime.Now;
}
如果您的异步方法正在执行CPU工作,则应使用Task.Run
:
private static async Task<DateTime> CountToAsync(int num = 10)
{
await Task.Run(() => ...);
return DateTime.Now;
}
您可能会发现我的async
/await
intro有帮助。
答案 1 :(得分:10)
如果您不想在方法中使用async / await,但仍然“装饰”它以便能够从外部使用await关键字TaskCompletionSource.cs:
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
为了支持使用Tasks的这种范例,我们需要一种方法来保留Taskfaçade以及将任意异步操作作为Task引用的能力,但是根据底层基础结构的规则来控制该Task的生命周期这提供了异步,并且以不显着的方式这样做。这是TaskCompletionSource的目的。
我看到也用于.NET源代码,例如。 WebClient.cs:
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<string>(address);
// Setup the callback event handler
UploadStringCompletedEventHandler handler = null;
handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
this.UploadStringCompleted += handler;
// Start the async operation.
try { this.UploadStringAsync(address, method, data, tcs); }
catch
{
this.UploadStringCompleted -= handler;
throw;
}
// Return the task that represents the async operation
return tcs.Task;
}
最后,我发现以下内容也很有用:
我一直被问到这个问题。这意味着必须有某个线程阻塞对外部资源的I / O调用。因此,异步代码释放了请求线程,但只是牺牲了系统中其他位置的另一个线程,对吧?一点都不。为了理解异步请求扩展的原因,我将跟踪异步I / O调用的(简化)示例。假设请求需要写入文件。请求线程调用异步写入方法。 WriteAsync由基类库(BCL)实现,并为其异步I / O使用完成端口。因此,WriteAsync调用作为异步文件写入传递给操作系统。然后,OS与驱动程序堆栈通信,传递数据以写入I / O请求数据包(IRP)。事情变得有趣:如果设备驱动程序无法立即处理IRP,它必须异步处理它。因此,驱动程序告诉磁盘开始写入并向操作系统返回“挂起”响应。操作系统将“挂起”响应传递给BCL,BCL将不完整的任务返回给请求处理代码。请求处理代码等待该任务,该任务从该方法返回不完整的任务,依此类推。最后,请求处理代码最终将一个不完整的任务返回给ASP.NET,并释放请求线程以返回到线程池。
Introduction to Async/Await on ASP.NET
如果目标是提高可伸缩性(而不是响应性),那么它都依赖于外部I / O的存在,这提供了实现这一目标的机会。
答案 2 :(得分:0)
使方法异步的一种非常简单的方法是使用Task.Yield()方法。如MSDN所述:
您可以使用await Task.Yield();在异步方法中强制 异步完成的方法。
在方法开始时插入它,然后它将立即返回给调用者,并在另一个线程上完成该方法的其余部分。
/* Header Logo */
.header-logo {
width:100%;
max-width: 160px;
}
/* Small devices (landscape phones, less than 768px) */
@media (max-width: 768px) {
.header-logo {
width:100%;
max-width: 80px;
margin:10px;
}
}
/* Large viewport navbar */
.nav-link {
font-family: 'Open Sans', sans-serif;
font-size: 0.8vw;
font-weight: bolder;
text-transform: uppercase;
}
.nav-link:hover {
border-bottom: 5px solid #ff7240;
color:#fff!important;
}