什么';这是新C#异步特性的一个很好的非网络化示例?

什么';这是新C#异步特性的一个很好的非网络化示例?,c#,c#-5.0,async-await,C#,C# 5.0,Async Await,微软刚刚宣布了这一计划。到目前为止,我看到的每个示例都是关于从HTTP异步下载内容的。当然还有其他重要的异步功能 假设我没有编写新的RSS客户端或Twitter应用程序。C#Async对我来说有什么有趣的地方 编辑我有一个哈哈哈!看电视的时候。在过去,我曾在使用“观察者”线程的程序上工作。这些线程等待某些事情发生,就像等待文件更改一样。它们没有做任何工作,只是处于空闲状态,并在发生某些事情时通知主线程。在新模型中,这些线程可以用wait/async代码替换。Ooh,这听起来很有趣。我不是在玩CT

微软刚刚宣布了这一计划。到目前为止,我看到的每个示例都是关于从HTTP异步下载内容的。当然还有其他重要的异步功能

假设我没有编写新的RSS客户端或Twitter应用程序。C#Async对我来说有什么有趣的地方


编辑我有一个哈哈哈!看电视的时候。在过去,我曾在使用“观察者”线程的程序上工作。这些线程等待某些事情发生,就像等待文件更改一样。它们没有做任何工作,只是处于空闲状态,并在发生某些事情时通知主线程。在新模型中,这些线程可以用wait/async代码替换。

Ooh,这听起来很有趣。我不是在玩CTP,只是在看白皮书。在看过之后,我想我可以看到它是如何被证明是有用的

据我所知,async使编写异步调用更易于阅读和实现。同样,现在编写迭代器更容易(而不是手工编写功能)。这是阻塞进程的关键,因为在解除阻塞之前,无法完成任何有用的工作。如果你正在下载一个文件,你不能做任何有用的事情,直到你得到那个文件,让线程浪费掉。考虑一下你如何调用一个函数,你知道它会阻塞一个不确定的长度,并返回一些结果,然后处理它(例如,将结果存储在一个文件中)。你会怎么写?下面是一个简单的例子:

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);
}
private async void doCalculation_Click(object sender, RoutedEventArgs e) {
    doCalculation.IsEnabled = false;
    await DoSomeBlockingOperationAsync(GetArgs());
    doCalculation.IsEnabled = true;
}
好的,很好,我们已经实施了。但是等等,计算需要几分钟才能完成。如果我们想要一个交互式应用程序,并在计算过程中做其他事情(例如呈现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);
}
如果您做过任何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();
}
阅读,它实际上有很多实用的例子,比如写并行任务和其他


我迫不及待地想在CTP或.NET 5.0最终成功时开始使用它。

今天我发现了另一个很好的使用案例:您可以等待用户交互

例如,如果一个窗体具有打开另一个窗体的按钮:

Form toolWindow;
async void button_Click(object sender, EventArgs e) {
  if (toolWindow != null) {
     toolWindow.Focus();
  } else {
     toolWindow = new Form();
     toolWindow.Show();
     await toolWindow.OnClosed();
     toolWindow = null;
  }
}
当然,这并不比

toolWindow.Closed += delegate { toolWindow = null; }
但我认为它很好地展示了
等待
可以做什么。一旦事件处理程序中的代码非常重要,
await
将使编程变得更加容易。设想用户必须单击一系列按钮:

async void ButtonSeries()
{
  for (int i = 0; i < 10; i++) {
    Button b = new Button();
    b.Text = i.ToString();
    this.Controls.Add(b);
    await b.OnClick();
    this.Controls.Remove(b);
  }
}
不幸的是,似乎无法将
GetAwaiter()
方法直接添加到
EventHandler
(允许
wait按钮。单击;
)中,因为这样该方法就不知道如何注册/取消注册该事件。 这有点像样板文件,但是AwaitableEvent类可以用于所有事件(不仅仅是UI)。只需稍加修改并添加一些泛型,就可以检索EventArgs:

MouseEventArgs e = await button.OnMouseDown();

我可以看到这对一些更复杂的UI手势(拖放、鼠标手势等)很有用,不过您必须添加取消当前手势的支持。

CTP中有一些示例和演示不使用网络,甚至有些不做任何I/O

它确实适用于所有多线程/并行问题领域(已经存在)


Async和Await是构造所有并行代码的一种新的(更简单的)方法,无论是CPU绑定还是I/O绑定。最大的改进是在C#5之前必须使用APM(IAsyncResult)模型或事件模型(BackgroundWorker、WebClient)的领域。我认为这就是为什么这些例子现在成为游行的主角。

主要场景是任何涉及高延迟的场景。也就是说,在“要求结果”和“获得结果”之间有很多时间。网络请求是高延迟场景中最明显的例子,紧随其后的通常是I/O,然后是CPU限制在另一个核心上的冗长计算

然而,这项技术可能会很好地适应其他场景。例如,考虑编写FPS游戏的逻辑。假设您有一个按钮单击事件处理程序。当玩家点击按钮时,你想鸣笛两秒钟以提醒敌人,然后开门十秒钟。说这样的话不是很好吗

button.Disable();
await siren.Activate(); 
await Delay(2000);
await siren.Deactivate();
await door.Open();
await Delay(10000);
await door.Close();
await Delay(1000);
button.Enable();

每个任务都在UI线程上排队,因此没有任何任务阻塞,并且每个任务在其任务完成后在正确的位置恢复单击处理程序。

GUI时钟就是一个很好的例子;假设你想画一个时钟,它每秒钟更新一次显示的时间。从概念上讲,您希望编写

 while true do
    sleep for 1 second
    display the new time on the clock
等待MouseEventArgs e = await button.OnMouseDown();
button.Disable();
await siren.Activate(); 
await Delay(2000);
await siren.Deactivate();
await door.Open();
await Delay(10000);
await door.Close();
await Delay(1000);
button.Enable();
 while true do
    sleep for 1 second
    display the new time on the clock
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace AsyncText
{
    class Program
    {
        static void Main(string[] args)
        {
            Derived d = new Derived();

            TaskEx.Run(() => d.DoStuff()).Wait();

            System.Console.Read();
        }
        public class Base
        {
            protected string SomeData { get; set; }

            protected async Task DeferProcessing()
            {
                await TaskEx.Run(() => Thread.Sleep(1) );
                return;
            }
            public async virtual Task DoStuff() {
                Console.WriteLine("Begin Base");
                Console.WriteLine(SomeData);
                await DeferProcessing();
                Console.WriteLine("End Base");
                Console.WriteLine(SomeData);
            }
        }
        public class Derived : Base
        {
            public async override Task DoStuff()
            {
                Console.WriteLine("Begin Derived");
                SomeData = "Hello";
                var x = base.DoStuff();
                SomeData = "World";
                Console.WriteLine("Mid 1 Derived");
                await x;
                Console.WriteLine("EndDerived");
            }
        }
    }
}