c# - 从同步操作方法中调用带有await的异步方法

标签 c# .net asp.net-mvc multithreading async-await

我需要实现启动处理开始的服务。但是我不需要等待结果。我只能显示默认输出,并且在后台运行该过程。
但是我想到了一个问题,即等待后的代码没有执行。
我准备了一些代码来展示这个想法:

public class HomeController : Controller
{
    public ActionResult Deadlock()
    {
        AsyncCall();

        return View();
    }

    private async Task AsyncCall()
    {
        await Task.Delay(10000);

        var nonreachablePlace = "The breakpoint will not set the execution here";

        Do(nonreachablePlace);
    }

    private void Do(string m)
    {
        m.Contains("x");
    }
}
我知道它看起来非常糟糕。但是这个主意是这样的:
  • 一个线程转到死锁方法。
  • 线程同步进入AsyncCall方法。
  • 面对await语句。
  • 从“死锁”方法开始。
  • 将main方法继续到最后。
  • Task.Delay完成后,该线程将从线程池中弹出并继续工作。

  • 到我的坏6步没有处理。我试图设置断点,但从未被击中。
    但是,如果我减少时间延迟并在调试中进行,则将进入第6步。
    Enter to the controller's action method
    After return from controller's action method
    但是,如果我在等待后仅留下一个断点,则不会执行6步var nonreachablePlace = "The breakpoint will not set the execution here";笔记:
    我将ConfigureAwait(false)附加到Task.Delay()。看起来像这样:
    private async Task AsyncCall()
    {
        await Task.Delay(10000).ConfigureAwait(false);
    
        var nonreachablePlace = "The breakpoint will not set the execution here";
    
        Do(nonreachablePlace);
    }
    
    
    现在,它可以按预期运行。
    我的问题是,如果没有ConfigureAwait(false),为什么代码不起作用?
    也许它与SynchronizationContext有关,并且在主线程完成其工作之后无法访问。然后在等待的方法尝试获取已经处理过的上下文之后(只是我的想法)

    最佳答案

    使用HostingEnvironment.QueueBackgroundWorkItem
    请注意,这仅在.NET Framework上的经典ASP.NET(System.Web.dll)中可用,而在ASP.NET Core中不可用(我忘记了它在.NET Framework上运行的ASP.NET Core 1.x和2.x中能在多大程度上起作用,但是反正)。
    您需要的是:

    using System.Web.Hosting;
    
    public class MyController : Controller
    {
        [HttpPost( "/foo" )]
        public async Task<ActionResult> DoSomething()
        {
            HostingEnvironment.QueueBackgroundWorkItem( this.DoSomethingExpensiveAsync ); // Pass the method by name or as a `Func<CancellationToken,Task>` delegate.
    
            return this.View();
        }
    
        private async Task DoSomethingExpensiveAsync( CancellationToken cancellationToken )
        {
            await Task.Delay( TimeSpan.FromSeconds( 30 ) );
        }
    }
    
    您还可以将其用于非异步工作负载:
        [HttpPost( "/foo" )]
        public async Task<ActionResult> DoSomething()
        {
            HostingEnvironment.QueueBackgroundWorkItem( this.DoSomethingExpensive ); // Pass the method by name or as a `Action<CancellationToken>` delegate.
    
            return this.View();
        }
    
        private void DoSomethingExpensive( CancellationToken cancellationToken )
        {
            Thread.Sleep( 30 * 1000 ); // NEVER EVER EVER call Thread.Sleep in ASP.NET!!! This is just an example!
        }
    
    如果您想一开始就正常地开始工作,并且如果花费的时间太长,则只能在后台完成它,那么请执行以下操作:
        [HttpPost( "/foo" )]
        public async Task<ActionResult> DoSomething()
        {
            Task<String> workTask    = this.DoSomethingExpensiveAsync( default );
            Task         timeoutTask = Task.Delay( TimeSpan.FromSeconds( 5 ) );
    
            Task first = await Task.WhenAny( workTask, timeoutTask );
            if( first == timeoutTask )
            {
                // `workTask` is still running, so resume it in the background:
                
                HostingEnvironment.QueueBackgroundWorkItem( async ct => await workTask );
    
                return this.View( "Still working..." );
            }
            else
            {
                // `workTask` finished before the timeout:
                String result = await workTask; // or just `workTask.Result`.
                return this.View( result );
            }
        }
    
        private async Task<String> DoSomethingExpensiveAsync( CancellationToken cancellationToken )
        {
            await Task.Delay( TimeSpan.FromSeconds( 30 ) );
    
            return "Explosive bolts, ten thousand volts; At a million miles an hour, Abrasive wheels and molten metals";
        }
    

    关于c# - 从同步操作方法中调用带有await的异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65587910/

    相关文章:

    c# - "HEAD"作为请求方法是否足以检查站点是否可达

    asp.net-mvc - 在 HttpModule 中获取执行操作

    c# - 定期托管服务是否需要保持事件状态?

    C#快速crc32计算:

    c# - 获取用户目录路径

    .net - 计算部分流的 MD5 哈希值

    asp.net-mvc - 使用 jquery ajax 传递多个复选框值

    javascript - 屏蔽剑道网格中的指定列

    c# - 通过循环检查列表框从数据库表中填充和删除项目

    c# - OwnerDraw 模式下的 ListBox DrawItem HotLight 状态?