天道酬勤,学无止境

如何等待异步委托(How to await on async delegate)

问题

在一个 MVA 视频中,我看到了下一个结构:

static void Main(string[] args)
{
    Action testAction = async () =>
    {
        Console.WriteLine("In");
        await Task.Delay(100);
        Console.WriteLine("After first delay");
        await Task.Delay(100);
        Console.WriteLine("After second delay");
    };

    testAction.Invoke();
}

执行结果将是:

In
Press any key to continue . . .

它完全编译,但现在我看不到任何等待它的方法。 我可能会在调用后放置Thread.SleepConsole.ReadKey ,但这不是我想要的。

那么这个委托应该如何修改为可等待的?(或者至少我如何跟踪执行完成?)

这样的代表有什么实际用途吗?

回答1

为了等待某事,它必须是awaitable 。 由于void并非如此,您不能等待任何Action委托。

awaitable 是实现GetAwaiter方法的任何类型,该方法返回实现INotifyCompletionICriticalNotifyCompletion的类型,例如TaskTask<T>

如果要等待委托,请使用Func<Task> ,它等效于具有以下签名的命名方法:

public Task Func()

因此,为了等待,请将您的方法更改为:

static void Main(string[] args)
{
    Func<Task> testFunc = async () =>
    {
        Console.WriteLine("In");
        await Task.Delay(100);
        Console.WriteLine("First delay");
        await Task.Delay(100);
        Console.WriteLine("Second delay");
    };
}

现在你可以等待它:

await testFunc();
回答2

"Async void 仅适用于顶级事件处理程序",

http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-handlers-only

回答3

最近我发现 NUnit 能够await async void测试。 这里很好地描述了它是如何工作的:nunit 如何成功等待异步 void 方法完成?

您不会在常规任务中使用它,但很高兴知道它是可能的

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 异步抛弃与“旧的异步委托”(Fire-and-forget with async vs “old async delegate”)
    问题 我试图用一种新的语法替换以前的“一劳永逸”的调用,希望更加简单,这似乎使我望而却步。 这是一个例子 class Program { static void DoIt(string entry) { Console.WriteLine("Message: " + entry); } static async void DoIt2(string entry) { await Task.Yield(); Console.WriteLine("Message2: " + entry); } static void Main(string[] args) { // old way Action<string> async = DoIt; async.BeginInvoke("Test", ar => { async.EndInvoke(ar); ar.AsyncWaitHandle.Close(); }, null); Console.WriteLine("old-way main thread invoker finished"); // new way DoIt2("Test2"); Console.WriteLine("new-way main thread invoker finished"); Console.ReadLine(); } } 两种方法都做同样的事情
  • 如何实现异步动作委托方法?(How do you implement an async action delegate method?)
    问题 一点背景资料。 我正在学习Web API堆栈,并试图以“结果”对象的形式封装所有数据,并带有诸如Success和ErrorCodes之类的参数。 但是,不同的方法会产生不同的结果和错误代码,但是结果对象通常将以相同的方式实例化。 为了节省时间,并进一步了解C#中的异步/等待功能,我试图将Web api操作的所有方法体包装在异步操作委托中,但遇到了一些麻烦... 给定以下类别: public class Result { public bool Success { get; set; } public List<int> ErrorCodes{ get; set; } } public async Task<Result> GetResultAsync() { return await DoSomethingAsync<Result>(result => { // Do something here result.Success = true; if (SomethingIsTrue) { result.ErrorCodes.Add(404); result.Success = false; } } } 我想编写一个对Result对象执行操作并返回它的方法。 通常通过同步方法 public T DoSomethingAsync<T>(Action<T> resultBody)
  • 如何“等待”引发EventHandler事件(How to 'await' raising an EventHandler event)
    问题 有时,事件模式由或子视图模型用于在MVVM应用程序中引发事件,以这种松散耦合的方式将消息发送到其父视图模型。 父视图模型 searchWidgetViewModel.SearchRequest += (s,e) => { SearchOrders(searchWidgitViewModel.SearchCriteria); }; SearchWidget ViewModel public event EventHandler SearchRequest; SearchCommand = new RelayCommand(() => { IsSearching = true; if (SearchRequest != null) { SearchRequest(this, EventArgs.Empty); } IsSearching = false; }); 在为.NET4.5重构我的应用程序时,我正在使尽可能多的代码可以使用async和await 。 但是以下方法不起作用(嗯,我真的没想到吗) await SearchRequest(this, EventArgs.Empty); 框架肯定是这样做的,以调用诸如此类的事件处理程序,但是我不确定它是如何做到的? private async void button1_Click(object sender
  • 如何等待C#中的事件?(How do I await events in C#?)
    问题 我正在创建一个包含一系列事件的类,其中之一是GameShuttingDown 。 当触发此事件时,我需要调用事件处理程序。 该事件的目的是通知用户游戏正在关闭,并且他们需要保存其数据。 可以等待保存,而不能等待事件。 因此,当处理程序被调用时,游戏将在等待的处理程序完成之前关闭。 public event EventHandler<EventArgs> GameShuttingDown; public virtual async Task ShutdownGame() { await this.NotifyGameShuttingDown(); await this.SaveWorlds(); this.NotifyGameShutDown(); } private async Task SaveWorlds() { foreach (DefaultWorld world in this.Worlds) { await this.worldService.SaveWorld(world); } } protected virtual void NotifyGameShuttingDown() { var handler = this.GameShuttingDown; if (handler == null) { return; } handler(this, new
  • 如何使用await在自定义TaskScheduler上运行Task?(How to run a Task on a custom TaskScheduler using await?)
    问题 我有一些返回Task<T>方法,可以随意await 。 我想让这些任务在自定义TaskScheduler上执行,而不是在默认TaskScheduler上执行。 var task = GetTaskAsync (); await task; 我知道我可以创建一个新的TaskFactory (new CustomScheduler ())并从中执行StartNew () ,但是StartNew ()会执行一个操作并创建Task ,并且我已经有了Task (由TaskCompletionSource返回到TaskCompletionSource ) 如何为await指定我自己的TaskScheduler ? 回答1 我认为您真正想要的是执行Task.Run ,但是要使用自定义调度程序。 StartNew不能直观地与异步方法一起使用。 Stephen Toub有一篇很棒的博客文章,介绍Task.Run和TaskFactory.StartNew之间的区别。 因此,要创建自己的自定义Run ,可以执行以下操作: private static readonly TaskFactory myTaskFactory = new TaskFactory( CancellationToken.None, TaskCreationOptions.DenyChildAttach
  • 如何使用异步方法正确编写Parallel.For(How to correctly write Parallel.For with async methods)
    问题 我将如何构造下面的代码,以便调用异步方法? Parallel.For(0, elevations.Count(), delegate(int i) { allSheets.AddRange(await BuildSheetsAsync(userID, elevations[i], includeLabels)); }); 回答1 Parallel.For()不适用于async方法。 如果您不需要限制并行度(例如,您可以同时执行所有任务),则只需启动所有Task ,然后等待它们完成即可: var tasks = Enumerable.Range(0, elevations.Count()) .Select(i => BuildSheetsAsync(userID, elevations[i], includeLabels)); List<Bitmap> allSheets = (await Task.WhenAll(tasks)).SelectMany(x => x).ToList(); 回答2 我建议您看一下我几天前问的这个问题,并最终回答自己,基本上我正在寻找并行和异步的ForEach方法。 该方法使用SemaphoreSlim并行处理事物,并且接受异步方法作为输入动作。 您可能还想看看我在回答结尾处提供的两个链接,它们对于实现这种行为确实很有帮助
  • 如何编写一个“ awaitable”方法?(How to write an “awaitable” method?)
    问题 我终于在研究async&await关键字了,我有点“ get”了,但是我看到的所有示例都在.Net框架中调用了异步方法,例如,这个示例调用了HttpClient.GetStringAsync() 。 我不太清楚的是这种方法中发生了什么,以及如何编写自己的“ awaitable”方法。 它像包装要在Task中异步运行的代码并将其返回一样简单吗? 回答1 就这么简单 Task.Run(() => ExpensiveTask()); 使其成为一种等待的方法: public Task ExpensiveTaskAsync() { return Task.Run(() => ExpensiveTask()); } 这里重要的是返回任务。 该方法甚至不必标记为异步。 (只需再读一点,便可以进入图片了) 现在这可以称为 async public void DoStuff() { PrepareExpensiveTask(); await ExpensiveTaskAsync(); UseResultsOfExpensiveTask(); } 请注意,此处的方法签名为async ,因为该方法可能会将控制权返回给调用方,直到ExpensiveTaskAsync()返回为止。 同样,在这种情况下,昂贵意味着像Web请求或类似请求一样耗时。 要将繁重的计算发送到另一个线程,通常最好使用“旧的”方法
  • 禁止显示“警告CS4014:因为未等待此调用,所以将继续执行当前方法…”(Suppressing “warning CS4014: Because this call is not awaited, execution of the current method continues…”)
    问题 这不是“如何在不等待的情况下安全地在C#中调用异步方法”的重复。 如何很好地抑制以下警告? 警告CS4014:因为未等待此调用,所以在调用完成之前,将继续执行当前方法。 考虑将“ await”运算符应用于调用结果。 一个简单的例子: static async Task WorkAsync() { await Task.Delay(1000); Console.WriteLine("Done!"); } static async Task StartWorkAsync() { WorkAsync(); // I want fire-and-forget // more unrelated async/await stuff here, e.g.: // ... await Task.Delay(2000); } 我尝试过但不喜欢的内容: static async Task StartWorkAsync() { #pragma warning disable 4014 WorkAsync(); // I want fire-and-forget here #pragma warning restore 4014 // ... } static async Task StartWorkAsync() { var ignoreMe = WorkAsync(); // I want
  • 在等待事件的同时如何等待WinForms中的信号?(How to wait for signal in WinForms while also listening to events?)
    问题 情况1 这是我的设置。 internal class MyClass { private ApiObject apiObject; private bool cond1; private bool cond2; internal MyClass() { this.apiObject = new ApiObject(); this.apiObject.ApiStateUpdate += new ApiStateUpdateEventHandler(ApiStateHandler); //wait for both conditions to be true } private void ApiStateHandler(string who, int howMuch) { if(who.Equals("Something") && howMuch == 1) this.cond1 = true; else if(who.Equals("SomethingElse") && howMuch == 1) this.cond2 = true; } } 如何等待两个条件都成立? 如果我做: while(!(this.cond1 && this.cond2)) { System.Threading.Thread.Sleep(1000); } ApiStateHandler(
  • 无法将lambda表达式转换为类型“…”,因为它不是委托类型(Cannot convert lambda expression to type “…” because it is not a delegate type)
    问题 再会! 我正在尝试使用lambda表达式编写一个匿名方法,该方法将从异步任务中返回一个对象。 我想在构造函数中执行此操作,因此这就是我无法使其父方法异步的原因。 ReadJsonAsync方法返回一个Session对象。 我将向您展示相关代码: Session session; fileService = new FileService(); session = async () => { return await fileService.ReadJsonAsync() }; 提前致谢! 回答1 如果您想要一个匿名方法,则必须声明一个返回Task<Session>因为它已用async修饰符标记,因此必须返回一个void (仅用于异步事件处理程序), Task或Task<T> : Func<Task<Session>> anonFunction = async () => await fileService.ReadJsonAsync(); 如果您所做的只是运行ReadJsonAsync ,那么您还可以像这样保存状态机的生成: Func<Task<Session>> anonFunction = fileService.ReadJsonAsync; 然后,您可以在更高的调用堆栈上await它: Func<Task<Session>> anonFunction =
  • 使用Asyc Await从事件更新UI(Updating UI from events using asyc await)
    问题 我试图了解如何在使用异步/等待模式时从事件更新UI。 以下是我在WinForm应用程序上使用的测试代码。 我什至不确定这是否是正确的方法。 允许pwe_StatusUpdate方法更新UI的必要条件是什么? 那里发生了跨线程操作错误。 谢谢阅读。 // calling code ProcessWithEvents pwe = new ProcessWithEvents(); pwe.StatusUpdate += pwe_StatusUpdate; await pwe.Run(); void pwe_StatusUpdate(string updateMsg) { // Error Here: Cross-thread operation not valid: Control '_listBox_Output' accessed from a thread other than the thread it was created on. _listBox_Output.Items.Add(updateMsg); } -- // Class with long running process and event public delegate void StatusUpdateHandler(string updateMsg); public class
  • .NET - Task.Run vs Task.Factory.StartNew
    在 .NET 4 中,Task.Factory.StartNew 是安排新任务的首选方法。它有许多重载提供了高度可配置的机制,通过启用设置选项,可以传递任意状态、启用取消,甚至控制调度行为。所有这些功能的另一面是复杂性。您需要知道什么时候使用哪个重载、提供什么调度程序等等。另外,Task.Factory.StartNew 用起来并不直截干脆,至少对于它的一些使用场景来说还不够快,比如它的主要使用场景——轻松地将工作交付到后台处理线程。因此,在 .NET Framework 4.5 开发者预览版 中,我们引入了新的 Task.Run 方法。这决不是要淘汰 Task.Factory.StartNew,而是应该简单地认为这是使用 Task.Factory.StartNew 而不必传递一堆参数的一个便捷方式。这是一个捷径。事实上,Task.Run 实际是按照与 Task.Factory.StartNew 相同的逻辑实现的,只是传入了一些默认的参数。当你传递一个 Action 给 Task.Run:Task.Run(someAction);完全等同于:Task.Factory.StartNew(someAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)
  • 我将如何运行异步任务方法同步?(How would I run an async Task<T> method synchronously?)
    问题 我正在学习异步/等待,并且遇到了需要同步调用异步方法的情况。 我怎样才能做到这一点? 异步方法: public async Task<Customers> GetCustomers() { return await Service.GetCustomersAsync(); } 正常用法: public async void GetCustomers() { customerList = await GetCustomers(); } 我尝试使用以下方法: Task<Customer> task = GetCustomers(); task.Wait() Task<Customer> task = GetCustomers(); task.RunSynchronously(); Task<Customer> task = GetCustomers(); while(task.Status != TaskStatus.RanToCompletion) 我也从这里尝试了一个建议,但是当调度程序处于挂起状态时,该建议将不起作用。 public static void WaitWithPumping(this Task task) { if (task == null) throw new ArgumentNullException(“task”); var nestedFrame =
  • 如何编写带有out参数的异步方法?(How to write an async method with out parameter?)
    问题 我想编写一个带有out参数的异步方法,如下所示: public async void Method1() { int op; int result = await GetDataTaskAsync(out op); } 如何在GetDataTaskAsync执行此操作? 回答1 您不能使用带有ref或out参数的异步方法。 Lucian Wischik解释了为什么无法在此MSDN线程上做到这一点:http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have参考或输出参数 至于为什么异步方法不支持按引用引用的参数? (或ref参数?)这是CLR的局限性。 我们选择以与迭代器方法类似的方式来实现异步方法-即通过编译器将方法转换为状态机对象。 CLR没有安全的方法将“输出参数”或“参考参数”的地址存储为对象的字段。 支持按引用引用参数的唯一方法是,如果异步功能是通过低级CLR重写而不是通过编译器重写来完成的。 我们研究了这种方法,并为此做了很多努力,但是最终它会非常昂贵,以至于从来没有发生过。 这种情况的典型解决方法是让async方法返回一个元组。 您可以这样重写方法: public async Task Method1
  • c#多线程总结(纯干货)
    线程基础 创建线程 static void Main(string[] args) { Thread t = new Thread(PrintNumbers); t.Start();//线程开始执行 PrintNumbers(); Console.ReadKey(); } static void PrintNumbers() { Console.WriteLine("Starting..."); for (int i = 1; i < 10; i++) { Console.WriteLine(i); } } 暂停线程 class Program { static void Main(string[] args) { Thread t = new Thread(PrintNumbersWithDelay); t.Start(); PrintNumbers(); Console.ReadKey(); } static void PrintNumbers() { Console.WriteLine("Starting..."); for (int i = 1; i < 10; i++) { Console.WriteLine(i); } } static void PrintNumbersWithDelay() { Console.WriteLine("Starting...")
  • 如何创建一个异步方法(How to create an asynchronous method)
    问题 我的C#应用​​程序中有一个简单的方法,它从FTP服务器中选择文件,然后对其进行解析并将数据存储在DB中。 我希望它是异步的,以便用户在App上执行其他操作,一旦解析完成,他就必须获得消息,指出“解析已完成”。 我知道可以通过异步方法调用来实现,但是我不知道该怎么做,任何人都可以帮助我吗? 回答1 您需要使用委托及其包含的BeginInvoke方法来异步运行另一个方法。 由委托运行的方法结束时,您可以通知用户。 例如: class MyClass { private delegate void SomeFunctionDelegate(int param1, bool param2); private SomeFunctionDelegate sfd; public MyClass() { sfd = new SomeFunctionDelegate(this.SomeFunction); } private void SomeFunction(int param1, bool param2) { // Do stuff // Notify user } public void GetData() { // Do stuff sfd.BeginInvoke(34, true, null, null); } } 在http://msdn.microsoft.com/zh-cn
  • 如何通过反射判断C#方法是否异步/等待?(How can I tell if a C# method is async/await via reflection?)
    问题 例如 class Foo { public async Task Bar() { await Task.Delay(500); } } 如果我们对此类和方法进行反思,我如何确定这是一个实际的async / await方法,而不只是一个恰好返回Task的方法? class Foo { public Task Bar() { return Task.Delay(500); } } 回答1 在我的代码副本中, async方法的MethodInfo在CustomAttributes属性中包含以下各项: DebuggerStepThroughAttribute 一个AsyncStateMachineAttribute 而普通方法的MethodInfo在其CustomAttributes属性中不包含任何项目。 似乎应该在异步方法而不是标准方法上可靠地找到AsyncStateMachineAttribute 。 编辑:实际上,该页面甚至在示例中具有以下内容! 如下例所示,您可以确定方法是用Async(Visual Basic)还是async(C#Reference)修饰符标记的。 在示例中,IsAsyncMethod执行以下步骤: 通过使用Type.GetMethod获取方法名称的MethodInfo对象。 通过使用GetType运算符(Visual Basic)或typeof(C#参考
  • C#有异步函数调用同步函数或同步函数调用异步函数(C# have async function call synchronous function or synchronous function call async function)
    问题 我正在编写一个 C# .Net 4.5 库,用于执行常见的 sql 数据库操作(备份、恢复、执行脚本等)。 我希望每个操作都有同步和异步函数,因为控制台和 GUI 应用程序都将使用这个库,但我不想在任何地方复制代码。 所以在我看来,我有两个选择: 编写在同步函数中完成工作的代码,然后将其包装在异步函数的任务中,如下所示: public void BackupDB(string server, string db) { // Do all of the work and long running operation here } public async Task BackupDBAsync(string server, string db) { await Task.Factory.StartNew(() => BackupDB(server, db)).ConfigureAwait(false); } 编写在异步函数中完成工作的代码,并使用 .Wait() 从同步函数调用它: public async Task BackupDBAsync(string server, string db) { // Do all of the work and long running operation here, asynchronously. } public void
  • C#GUI刷新和异步串行端口通信(C# GUI refresh and async serial port communication)
    问题 我正在尝试创建一个通过串行端口与硬件通信并将结果报告给gui的应用程序。 当前在GUI中移动是由KeyEvents进行的,这些事件触发GUI的下一个“页面”的绘制。 但是,第一步(按下键后),我需要绘制新页面并通过串行端口发送一些命令。 通过以下命令发送命令: port.Write(data, 0, data.Length); 然后,我通过等待DataReceivedHandler触发来等待答案-它只是指出正在等待数据并且正在使用另一种方法来处理数据。 起初,我只是在“绘制零件”之后的页面绘制函数中放置了发送和接收命令,但是这使它卡住了-数据正在传输,但页面没有绘制-它被冻结了。 然后我做了一个异步方法: private async void SendData() { await Task.Run(() => serialClass.SendAndReceive(command)); // process reply etc. } 那样使用: public void LoadPage() { image = Image.FromFile(path); //do some stuff on image using Graphics, adding texts etc. picturebox1.Image = image; SendData(); } 它工作正常,但是我需要
  • RunAsync-如何等待UI线程上的工作完成?(RunAsync - How do I await the completion of work on the UI thread?)
    问题 在等待Dispatcher.RunAsync ,继续是在安排工作时发生,而不是在工作完成时发生。 我如何等待工作完成? 编辑 我最初的问题假定过早的延续是由API的设计引起的,所以这才是真正的问题。 当使用异步委托在代理的代码中使用await来等待Dispatcher.RunAsync时,延续将await遇到await时发生,而不是在工作完成时发生。 我如何等待工作完成? 编辑2 您可能需要分派UI线程上已经存在的工作的原因之一是要解决细微的时间安排和布局问题。 视觉树中元素的大小和位置的值不断变化是很常见的,为UI的后续迭代安排工作可能会有所帮助。 回答1 我在Microsoft github存储库上发现了以下建议:如何等待从后台线程发送的UI任务。 设置 为CoreDispatcher定义此扩展方法: using System; using System.Threading.Tasks; using Windows.UI.Core; public static class DispatcherTaskExtensions { public static async Task<T> RunTaskAsync<T>(this CoreDispatcher dispatcher, Func<Task<T>> func, CoreDispatcherPriority