【问题标题】:Running a Task in the background (PCL)在后台运行任务 (PCL)
【发布时间】:2015-03-13 09:46:32
【问题描述】:

我有一个带有async 方法的类:

public static async Task GetData() { ... }

在我正在使用的应用程序框架中,我需要启动该进程并在应用程序启动时忘记它:

protected override void OnStart()
{
    await MyService.GetData();
}

我无法制作OnStart async. 如何在后台任务中启动它而忘记它?

【问题讨论】:

  • 任务在后台线程上运行。你在问什么? GetData在启动异步部分之前是否做了很多同步处理?

标签: c# .net task-parallel-library async-await task


【解决方案1】:

我无法使 OnStart 异步。如何在后台任务中启动它和 忘记了吗?

为什么不呢?没有什么能阻止你做到asyncasync 修饰符不会影响 CLR 方法签名,即您可以覆盖 void 方法并使其成为 async

abstract class AppBase
{
    protected abstract void OnStart();
}

class App: AppBase
{
    public static async Task GetData() { await Task.Delay(1); }

    protected override async void OnStart()
    {
        await GetData(); 
    }
}

这样,如果GetData 抛出,至少你会看到一个异常,这与其他答案所暗示的不同。

确保您了解async void 方法和Task 错误处理的一般工作原理,this material 可能会有所帮助。

Task.Run( () => MyService.GetData() ) 的一些其他问题:

  • 因为GetData已经异步的,用Task.Run 包装它几乎没有意义。它通常仅在客户端 UI 应用程序中完成,并且仅当 GetData 具有长时间运行的同步部分(在它达到其第一个 await 之前)。否则,您最好在没有Task.Run 和没有await 的情况下调用GetData()(这也是一个坏主意:在任何一种情况下,您都会在没有观察到可能的异常的情况下进行即发即弃的调用) .

  • Task.Run 将在没有同步内容的随机池线程上启动 GetData,这对于 UI 应用程序或 ASP.NET 应用程序都可能是个问题。

【讨论】:

  • 我认为这是个坏主意(这里是why
  • 当你不在乎它何时完成时,为什么要await它?只需调用该方法并忽略Task
  • @l3arnon,我的答案中有一个链接,其中详细介绍了 async void 方法的工作原理,包括异常。 OP 是否想在这里处理异常(参见 Eric Lippert 的Vexing exceptions)或者他是否想使进程崩溃取决于他。关键是异常不会被忽视。
  • @Servy,请参阅我上面的评论。如需更详细的讨论,请参阅this
  • @Noseratio 最不理想的行为确实是忽略异常,而最重要的是要拆除整个过程。有很多方法可以不忽略异常:2 个事件,try-catch 内部,延续和崩溃是最不愉快和最危险的。
【解决方案2】:

如果您想触发这个async 操作并忘记它,您只需调用该方法而不等待返回的任务:

protected override void OnStart()
{
    MyService.GetDataAsync();
}

但是,由于您没有观察任务,因此您永远不会知道它是否成功完成。

您应该保留对任务的引用,并在以后await它:

public Task _dataTask;
protected override void OnStart()
{
    _dataTask = MyService.GetDataAsync();
}

public Task AwaitInitializationAsync()
{
    return _dataTask;
}

或者添加一个继续处理任何异常:

protected override void OnStart()
{
    MyService.GetDataAsync().ContinueWith(t => 
    {
        try 
        { 
            t.Wait();
        }
        catch (Exception e)
        {
            // handle exceptions
        }
    });
}

您不应该将Task.Run 用作Noseratio explained,但是使用async void 会更糟糕,因为async void 方法(不是UI 事件处理程序)中的异常会拆除整个过程*

您可以尝试创建async void 方法,同时使用try-catch 块确保其中不会引发任何异常:

protected override async void OnStart()
{
    try
    {
        await GetData(); 
    }
    catch (Exception e)
    {
        // handle e.
    }
}

但我仍然建议不要这样做,因为即使完全崩溃的可能性也是危险的。


*您可以通过为AppDomain.CurrentDomain.UnhandledException 注册偶数处理程序来解决此问题,但这应该是最后的手段,而不是最佳实践

【讨论】:

  • OP 是否要处理异常或终止进程取决于他,请参阅我的回答的评论。最不理想的行为是忽略即发即弃任务的异常,这是我的观点。也就是说,如果他确实想处理它们,为什么要使用ContinueWith 而不仅仅是async voidawait 里面try/catch 里面?
  • 很高兴看到您进行了编辑。我几乎想不出ContinueWithasync/await 有任何优势或不能被async/await 取代的任何用例。特别是,请牢记延续中错误处理的含义。
  • @Noseratio 我当然对async-await 没有任何问题,但async void 是为UI 事件处理程序设计的,一般不建议在其他地方使用。
  • 我想说,不仅是 UI 事件处理程序,还有任何类型的事件处理程序。就个人而言,我不认为这是一个教条。我还将它用于On 之类的可覆盖通知,如 OP 的 OnStart 和讨论的即发即弃任务 here
猜你喜欢
  • 1970-01-01
  • 2020-10-24
  • 1970-01-01
  • 2013-10-01
  • 2016-09-05
  • 2015-05-05
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
相关资源
最近更新 更多