哦,这听起来很有趣。我还没有玩CTP,只是审查了白皮书。在看到Anders Hejlsberg's talk之后,我想我可以看到它是如何有用的。
据我所知,异步使编写异步调用更容易阅读和实现。非常类似于现在编写迭代器更容易(而不是用手写出功能)。这是基本的阻止流程,因为在解除阻止之前,无法进行有用的工作。如果你正在下载一个文件,你不能做任何有用的事情,直到你得到该文件让线程浪费。考虑如何调用一个你知道会阻塞一个不确定长度并返回一些结果然后处理它的函数(例如,将结果存储在一个文件中)。你会怎么写?这里有一个简单的例子:
static object DoSomeBlockingOperation(object args)
{
// block for 5 minutes
Thread.Sleep(5 * 60 * 1000);
return args;
}
static void ProcessTheResult(object result)
{
Console.WriteLine(result);
}
static void CalculateAndProcess(object args)
{
// let's calculate! (synchronously)
object result = DoSomeBlockingOperation(args);
// let's process!
ProcessTheResult(result);
}
好的,我们已经实施了。但等等,计算需要几分钟才能完成。如果我们想要交互式应用程序并在计算发生时执行其他操作(例如呈现UI),该怎么办?这是不好的,因为我们同步调用函数,并且我们必须等待它有效地冻结应用程序,因为线程正在等待被解除阻塞。
答案,异步调用函数的函数。这样我们就不一定要等待阻塞操作完成。但我们如何做到这一点?我们会异步调用该函数,并注册一个回调函数,在解锁时调用,以便处理结果。
static void CalculateAndProcessAsyncOld(object args)
{
// obtain a delegate to call asynchronously
Func<object, object> calculate = DoSomeBlockingOperation;
// define the callback when the call completes so we can process afterwards
AsyncCallback cb = ar =>
{
Func<object, object> calc = (Func<object, object>)ar.AsyncState;
object result = calc.EndInvoke(ar);
// let's process!
ProcessTheResult(result);
};
// let's calculate! (asynchronously)
calculate.BeginInvoke(args, cb, calculate);
}
- 注:当然,我们可以启动另一个线程来做到这一点,但是这将意味着我们正在产卵一个线程,只是坐在那里等着畅通,然后做一些有益的工作。这将是一种浪费。
现在调用是异步的,我们不必担心等待计算完成和处理,它是异步完成的。它会在完成时完成。除了直接调用代码之外,您可以使用任务:
static void CalculateAndProcessAsyncTask(object args)
{
// create a task
Task<object> task = new Task<object>(DoSomeBlockingOperation, args);
// define the callback when the call completes so we can process afterwards
task.ContinueWith(t =>
{
// let's process!
ProcessTheResult(t.Result);
});
// let's calculate! (asynchronously)
task.Start();
}
现在我们异步调用函数。但是,为了达到这个目的,需要做些什么?首先,我们需要委托/任务能够异步调用它,我们需要一个回调函数来处理结果,然后调用函数。我们已经将两行函数调用变成了更多,只是为了异步调用某些东西。不仅如此,代码中的逻辑变得越来越复杂了。虽然使用任务有助于简化流程,但我们仍然需要做一些事情才能实现。我们只是想异步运行然后处理结果。为什么我们不能这样做?现在好了,我们可以:
// need to have an asynchronous version
static async Task<object> DoSomeBlockingOperationAsync(object args)
{
//it is my understanding that async will take this method and convert it to a task automatically
return DoSomeBlockingOperation(args);
}
static async void CalculateAndProcessAsyncNew(object args)
{
// let's calculate! (asynchronously)
object result = await DoSomeBlockingOperationAsync(args);
// let's process!
ProcessTheResult(result);
}
现在,这是通过简单的操作(计算,过程)非常简单的例子。想象一下,如果每个操作都不能方便地放入单独的函数中,而是有数百行代码。这只是为了获得异步调用的好处而增加了很多复杂性。
白皮书中使用的另一个实际示例是在UI应用程序中使用它。修改为使用上面的例子:
private async void doCalculation_Click(object sender, RoutedEventArgs e) {
doCalculation.IsEnabled = false;
await DoSomeBlockingOperationAsync(GetArgs());
doCalculation.IsEnabled = true;
}
如果你做任何UI编程(无论是的WinForms或WPF),并试图处理程序中调用一个函数昂贵,你就会知道这是很方便的。因为后台线程将坐在那里等待,直到它可以工作,使用后台工作者不会有多大的帮助。
假设您有办法控制某些外部设备,比如打印机。你想在失败后重新启动设备。当然,打印机启动并准备好运行需要一些时间。您可能必须考虑重新启动而不能帮助并尝试重新启动。你别无选择,只能等待它。不是如果你异步做它。
static async void RestartPrinter()
{
Printer printer = GetPrinter();
do
{
printer.Restart();
printer = await printer.WaitUntilReadyAsync();
} while (printer.HasFailed);
}
想象写循环,不异步。
我有最后一个例子。想象一下,如果你不得不在一个函数中执行多个阻塞操作,并想异步调用。你更喜欢什么?
static void DoOperationsAsyncOld()
{
Task op1 = new Task(DoOperation1Async);
op1.ContinueWith(t1 =>
{
Task op2 = new Task(DoOperation2Async);
op2.ContinueWith(t2 =>
{
Task op3 = new Task(DoOperation3Async);
op3.ContinueWith(t3 =>
{
DoQuickOperation();
}
op3.Start();
}
op2.Start();
}
op1.Start();
}
static async void DoOperationsAsyncNew()
{
await DoOperation1Async();
await DoOperation2Async();
await DoOperation3Async();
DoQuickOperation();
}
阅读whitepaper,它实际上有很多实际的例子,比如编写并行任务等。
我迫不及待地想要在CTP或.NET 5.0最终实现时开始玩这个游戏。
或者更一般地提出这个问题,何时async有用?几乎任何一般的异步示例都应该与C#5中显示的内容相关。 – Larsenal 2010-10-28 23:05:43
当您说“非联网”时,您是否确实指“非I/O”?因为实际上所有的I/O都可能阻塞。 – Gabe 2010-10-29 03:10:25
+1假设我正在处理来自网络的数据流,但是通过3D方终端进行处理,我不知道终端内部或终端是如何从网络获取数据,我只是使用它的dll并获取所有数据在我的程序中异步显示为魔法。不需要知道关于网络的任何信息。但我需要知道异步/等待 – 2013-04-08 09:58:30