Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从同步操作方法调用包含wait的异步方法_C#_.net_Asp.net Mvc_Multithreading_Async Await - Fatal编程技术网

C# 从同步操作方法调用包含wait的异步方法

C# 从同步操作方法调用包含wait的异步方法,c#,.net,asp.net-mvc,multithreading,async-await,C#,.net,Asp.net Mvc,Multithreading,Async Await,我需要实现一个启动处理的服务。但我不需要等待结果。我可以只显示默认输出,在后台进程将正常运行。 但是我遇到了一个问题,wait之后的代码没有执行 我准备了一些代码来展示这个想法: public class HomeController : Controller { public ActionResult Deadlock() { AsyncCall(); return View(); } private async Task A

我需要实现一个启动处理的服务。但我不需要等待结果。我可以只显示默认输出,在后台进程将正常运行。
但是我遇到了一个问题,wait之后的代码没有执行

我准备了一些代码来展示这个想法:

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方法
  • 面临等待声明
  • 从死锁方法开始
  • 将main方法继续到结尾
  • 当Task.Delay完成时,该线程将从线程池中出现并继续工作
  • 到我的坏6步骤是没有处理。我试图设置断点,但从未被击中

    但是如果我减少时间延迟并在调试中执行,我将进入第6步。

    但是如果我在等待后只留下一个断点,我就不会进入第6步
    var nonrechableplace=“断点不会在此处设置执行”

    注:
    我将
    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);
    }
    
    
    现在它的工作原理与预期一致

    我的问题是,如果没有ConfigureWait(false),代码为什么不能工作?
    可能它与同步上下文有关,并且在主线程完成其工作后无法访问它。在这个等待的方法之后,尝试在上下文已经被释放时获取上下文(我的想法是这样的)

    So
    Deadlock()
    calls
    AsyncCall()

    然后
    AsyncCall()
    告诉
    DeadLock()
    “好的,我正在等待
    任务。延迟
    计数到10000,但您可以继续。”
    …因此
    AsyncCall()
    将主线程返回到
    DeadLock()

    现在,
    DeadLock()
    从未说过要等待
    AsyncCall()
    完成,所以就
    DeadLock()
    而言,
    AsyncCall()
    已经返回了(它实际上只是产生了,但程序游标仍然会传递回
    DeadLock()

    因此,我建议在
    DeadLock()
    中的
    AsyncCall()
    方法处设置断点,因为您可能会看到在
    Task.Delay()
    完成之前,主线程已经完成并退出


    因此,
    AsyncCall()
    甚至没有机会完成等待的

    使用
    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 ) );
        }
    }
    
    使用System.Web.Hosting;
    公共类MyController:Controller
    {
    [HttpPost(“/foo”)]
    公共异步任务DoSomething()
    {
    HostingEnvironment.QueueBackgroundWorkItem(this.doSomethingeExpensiveAsync);//按名称或作为'Func'委托传递方法。
    返回这个.View();
    }
    专用异步任务DoSomethingeExpensiveAsync(CancellationToken CancellationToken)
    {
    等待任务延迟(时间跨度从秒(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”)]
    公共异步任务DoSomething()
    {
    HostingEnvironment.QueueBackgroundWorkItem(this.doSomethingeExpensive);//按名称或作为'Action'委托传递方法。
    返回这个.View();
    }
    私有void DoSomethingExpensive(CancellationToken CancellationToken)
    {
    Thread.Sleep(30*1000);//永远不要在ASP.NET中调用Thread.Sleep!!!这只是一个例子!
    }
    
    如果您想在开始时正常启动作业,并且只在需要较长时间的情况下在后台完成作业,请执行以下操作:

        [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";
        }
    
    [HttpPost(“/foo”)]
    公共异步任务DoSomething()
    {
    Task workTask=this.DoSomethingExpensiveAsync(默认值);
    任务超时任务=任务延迟(TimeSpan.FromSeconds(5));
    任务优先=等待任务。WhenAny(工作任务,超时任务);
    if(first==超时任务)
    {
    //'workTask'仍在运行,请在后台继续运行:
    HostingEnvironment.QueueBackgroundWorkItem(异步ct=>Wait workTask);
    返回此。视图(“仍在工作…”);
    }
    其他的
    {
    //`workTask`在超时之前完成:
    String result=await workTask;//或仅“workTask.result”。
    返回此.视图(结果);
    }
    }
    专用异步任务DoSomethingeExpensiveAsync(CancellationToken CancellationToken)
    {
    等待任务延迟(时间跨度从秒(30));
    返回“爆炸螺栓,一万伏特;以每小时一百万英里的速度,磨轮和熔化的金属”;
    }
    
    我深入研究了
    任务背后的逻辑。延迟(10000)
    以及之后的继续代码。
    多亏了Stephen Toub的作品。
    主要问题部分是当任务完成时。它的结果需要由下一个线程处理。
    由于我没有编写
    ConfigureAwait()
    ,我暗指在具有
    SynchronizationContext
    AspNetSynchronizationContext
    )的线程中运行代码

    因为我不想等待等待的结果,所以我从控制器的操作返回了响应。然后线程转到ThreadPool,SynchronizationContext被释放。 到任务完成时,还没有SynchronizationContext可以发送带有延续代码的委托。

    在代码创建过程中,Visual Studio
    仅启用了“我的代码”
    选项设置为true
    private async Task AsyncCall()
    {
        /// The delay is done by a thread from a ThreadPool.
        await Task.Delay(10000);
    
        /// After the Task has been finished
        /// TaskAwaiter tryies to send a continuation code to a thread with
        /// SynchronizationContext.
        var nonreachablePlace = "The breakpoint will not set the execution here";
    
        Do(nonreachablePlace);
    }