【问题标题】:Call an async Task from a sync Method without needing any return (Result) [duplicate]从同步方法调用异步任务而不需要任何返回(结果)[重复]
【发布时间】:2020-07-15 00:07:51
【问题描述】:

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

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

例子:

//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 - 我之前在这里搜索过,但仍然没有找到明确的答案

【问题讨论】:

  • CancellationToken 来自哪里?
  • 否,因为如果没有发送,构造函数会构建一个默认值。
  • 为什么MySyncMethod需要同步?
  • @RuiCaramalho 你想在构造函数中调用它吗?如果是这样,那么有比阻塞或即发即弃更清洁的解决方案。
  • 您想每隔X 秒发送一些消息以保持...某些...活着,并且您不想阻塞主线程?有什么理由不使用System.Threading.Timer

标签: c# asynchronous


【解决方案1】:

到目前为止,最好的解决方案是使MySyncMethodasync

如果由于某种原因他不可能,下一个最佳解决方案可能是通过将 Task.Delay 替换为 Thread.Sleep 来使 KeepAlive 同步。

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

但是,您可以通过简单地丢弃 Task 来做到这一点:

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

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

如果您的应用没有同步上下文,您可以简单地使用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 方法更明确。

【讨论】:

  • 我在类的构造函数中使用KeepAlive()(我认为我不能使构造函数异步?)。我的另一个疑问是?当使用'KeepAlive(keepAliveSec).Wait();'我们说要等待结果。这不会造成任何问题吗?与.ConfigureAwait(false);相比?
  • 澄清一下,KeepAlive() 任务的想法很火,忘记我无意得到任何结果......
  • @RuiCaramalho 你不能使构造函数async,但你可以存储Task 并稍后等待。我将添加一个简单的示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-08
  • 2015-09-11
  • 2021-07-31
相关资源
最近更新 更多