首页 > 解决方案 > 从同步方法调用异步任务而不需要任何返回(结果)

问题描述

场景:我想从同步方法调用异步任务,但它是一个独立的任务,我不需要任何返回,我计划控制任务中的所有异常,因此不会启动任何异常。

什么是最好的解决方案来调用它而不会出现死锁并试图花费更少的资源?

例子:

//Sync Method
public MySyncMethod()
{
    //Maybe this?
    Task.Run(() => KeepAlive(keepAliveSec));
    //or this?
    KeepAlive(keepAliveSec).GetAwaiter();
    //Any other better option?
}

/// <summary>
/// Starts Heartbeat, Async Task
/// </summary>
/// <param name="sec"></param>
/// <returns></returns>
public async Task KeepAlive(int sec)
{
    try
    {
        while (!CancelationToken.IsCancellationRequested)
        {
            if ((DateTime.Now - LastMessageDate).Seconds<sec)
            {
                SendMessage(new MessageV4() { MsgType = MsgTypeEnum.HeartBeat });
            }
            await Task.Delay(60000);
        }
    }
    catch (Exception ex)
    {
        //Just do what you want to control any Exception
        Log.Debug(ex);
    }
}

PS - 我以前在这里搜索过,但我仍然没有找到明确的答案

标签: c#asynchronous

解决方案


到目前为止,最好的解决方案是制作MySyncMethod async.

如果由于某种原因他是不可能的,下一个最好的解决方案可能是KeepAlive通过替换来Task.Delay进行同步Thread.Sleep

无论哪种方式,如果MySyncMethod要同步运行,您将不得不在某个时候阻塞线程,除非您正在执行即发即弃,这很少是一个好主意;即使您有 100% 的信心KeepAlive永远不会抛出异常,但进程总是有可能在Task完成之前终止。

但是,您可以通过简单地丢弃Task

public MySyncMethod()
{
    _ = KeepAlive(keepAliveSec);
}

如果这些选项都不适合,那么您就进入了变通方法的领域。

如果您的应用程序没有同步上下文,您可以简单地阻止 using Wait(),而不会有死锁的风险:

public MySyncMethod()
{
    KeepAlive(keepAliveSec).Wait();
}

如果您确实有同步上下文,那么您可以Task.Run()用来卸载到线程池:

public MySyncMethod()
{
    Task.Run(() => KeepAlive(keepAliveSec)).Wait();
}

虽然使用这种方法KeepAlive将无法更新 UI,或访问HttpContext.

它还引入了开销,并同时使用了 2 个线程,因此只能作为最后的手段。


如果MySyncMethod实际上是一个构造函数,你可以这样做:

class MyClass
{
    Task task;
    
    MyClass(int keepAliveSec)
    {
        task = KeepAlive(keepAliveSec);
    }

    Task WaitAsync() => task;
}

然后你可以像这样使用:

var myClass = new MyClass(10000);

// Do some other stuff..

// Make sure myClass has completed before exiting
await myClass.WaitAsync();

当然,您可以只task公开该字段,但有一个WaitAsync方法更为明确。


推荐阅读