Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.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# 导致死锁的异步/等待示例_C#_Task Parallel Library_Deadlock_Async Await_C# 5.0 - Fatal编程技术网

C# 导致死锁的异步/等待示例

C# 导致死锁的异步/等待示例,c#,task-parallel-library,deadlock,async-await,c#-5.0,C#,Task Parallel Library,Deadlock,Async Await,C# 5.0,我遇到了一些使用c#的async/await关键字进行异步编程的最佳实践(我是c#5.0新手) 其中一项建议如下: 稳定性:了解您的同步上下文 。。。 某些同步上下文是不可重入的和单线程的。这意味着在给定时间只能在上下文中执行一个工作单元。例如Windows UI线程或ASP.NET请求上下文。 在这些单线程同步上下文中,您很容易死锁。如果从单线程上下文派生任务,然后在上下文中等待该任务,那么等待的代码可能会阻塞后台任务 public ActionResult ActionAsync() {

我遇到了一些使用c#的
async
/
await
关键字进行异步编程的最佳实践(我是c#5.0新手)

其中一项建议如下:

稳定性:了解您的同步上下文

。。。 某些同步上下文是不可重入的和单线程的。这意味着在给定时间只能在上下文中执行一个工作单元。例如Windows UI线程或ASP.NET请求上下文。 在这些单线程同步上下文中,您很容易死锁。如果从单线程上下文派生任务,然后在上下文中等待该任务,那么等待的代码可能会阻塞后台任务

public ActionResult ActionAsync()
{
    // DEADLOCK: this blocks on the async task
    var data = GetDataAsync().Result;

    return View(data);
}

private async Task<string> GetDataAsync()
{
    // a very simple async method
    var result = await MyWebService.GetDataAsync();
    return result.ToString();
}
public ActionResult ActionAsync()
{
//死锁:这会阻塞异步任务
var data=GetDataAsync().Result;
返回视图(数据);
}
专用异步任务GetDataAsync()
{
//一个非常简单的异步方法
var result=await MyWebService.GetDataAsync();
返回result.ToString();
}
如果我自己尝试剖析它,主线程将在
MyWebService.GetDataAsync()中生成一个新线程,但由于主线程在那里等待,因此它在
GetDataAsync()中等待结果。同时,我们说数据已经准备好了。为什么主线程不继续它的继续逻辑并从
GetDataAsync()
返回字符串结果

有人能解释一下为什么在上面的例子中会出现死锁吗? 我完全不知道问题是什么…

看看,斯蒂芬有一个明确的答案:

这就是发生的情况,从顶层方法开始(
Button1\u单击
UI/
MyController.Get
ASP.NET):

  • 顶级方法调用
    GetJsonAsync
    (在UI/ASP.NET上下文中)

  • GetJsonAsync
    通过调用
    HttpClient.GetStringAsync
    (仍在上下文中)启动REST请求

  • GetStringAsync
    返回未完成的
    任务
    ,表示REST请求未完成

  • GetJsonAsync
    等待
    GetStringAsync
    返回的
    Task
    。该上下文被捕获,稍后将用于继续运行
    GetJsonAsync
    方法
    GetJsonAsync
    返回未完成的
    任务
    ,表示
    GetJsonAsync
    方法未完成

  • 顶级方法同步阻止由
    GetJsonAsync
    返回的
    任务。这将阻止上下文线程

  • 。。。最终,REST请求将完成。这就完成了由
    GetStringAsync
    返回的
    任务

  • GetJsonAsync
    的继续现在可以运行了,它等待上下文可用,以便可以在上下文中执行

  • 死锁。顶级方法正在阻止上下文线程,等待
    GetJsonAsync
    完成,而
    GetJsonAsync
    正在等待上下文释放以便完成。对于UI示例,“上下文”是UI上下文;对于ASP.NET示例,“上下文”是ASP.NET请求上下文。这种类型的死锁可以由“上下文”或“上下文”引起


  • 您应该阅读的另一个链接:

    另一个要点是,您不应该阻塞任务,而应该一直使用异步来防止死锁。然后它将是异步的,而不是同步的阻塞

    public async Task<ActionResult> ActionAsync()
    {
    
        var data = await GetDataAsync();
    
        return View(data);
    }
    
    private async Task<string> GetDataAsync()
    {
        // a very simple async method
        var result = await MyWebService.GetDataAsync();
        return result.ToString();
    }
    
    公共异步任务ActionAsync()
    {
    var data=await GetDataAsync();
    返回视图(数据);
    }
    专用异步任务GetDataAsync()
    {
    //一个非常简单的异步方法
    var result=await MyWebService.GetDataAsync();
    返回result.ToString();
    }
    
    • 事实1:
      GetDataAsync().ResultGetDataAsync()
      返回的任务完成时,code>将运行,同时它会阻塞UI线程
    • 事实2:等待的继续(
      返回结果.ToString()
      )排队等待UI线程执行
    • 事实3:
      GetDataAsync()
      返回的任务将在其排队继续运行时完成
    • 事实4:队列延续永远不会运行,因为UI线程被阻塞(事实1)
    僵局 可以通过提供替代方案来打破僵局,以避免事实1或事实2

    • 避免1,4。使用
      var data=await GetDataAsync()
      ,而不是阻塞UI线程,这允许UI线程保持运行
    • 避免2,3。将等待的延续排队到未被阻止的其他线程,例如,使用
      var data=Task.Run(GetDataAsync).Result
      ,将延续发布到线程池线程的同步上下文。这允许
      GetDataAsync()
      返回的任务完成

    这在一篇文章中得到了很好的解释,大约在文章的一半,他使用了
    DelayAsync()

    的例子,我只是在ASP.NET MVC项目中再次处理这个问题。当您想从
    PartialView
    调用
    async
    方法时,不允许将
    PartialView
    设置为async
    。如果你这样做,你会得到一个例外

    在希望从同步方法调用
    async
    方法的场景中,可以使用以下简单的解决方法:

  • 在调用之前,请清除
    SynchronizationContext
  • 执行该调用,这里将不再出现死锁,等待它完成
  • 恢复同步上下文
  • 例如:

    public ActionResult DisplayUserInfo(string userName)
    {
        // trick to prevent deadlocks of calling async method 
        // and waiting for on a sync UI thread.
        var syncContext = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(null);
    
        //  this is the async call, wait for the result (!)
        var model = _asyncService.GetUserInfo(Username).Result;
    
        // restore the context
        SynchronizationContext.SetSynchronizationContext(syncContext);
    
        return PartialView("_UserInfo", model);
    }
    

    我遇到的一个解决方法是在请求结果之前对任务使用
    Join
    扩展方法

    代码如下所示:

    public ActionResult ActionAsync()
    {
      var task = GetDataAsync();
      task.Join();
      var data = task.Result;
    
      return View(data);
    }
    
    其中连接方法为:

    public static class TaskExtensions
    {
        public static void Join(this Task task)
        {
            var currentDispatcher = Dispatcher.CurrentDispatcher;
            while (!task.IsCompleted)
            {
                // Make the dispatcher allow this thread to work on other things
                currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle);
            }
        }
    }
    
    我在这方面做得不够