C# 这可以吗;同步;代码导致死锁?

C# 这可以吗;同步;代码导致死锁?,c#,asp.net,async-await,C#,Asp.net,Async Await,我的公司有一个他们编写的Nuget软件包,可以轻松地为您完成各种常见任务。其中之一就是发出HTTP请求。通常,我总是使我的HTTP请求异步,但是在这个Nuget包中有以下代码: protected T GetRequest<T>(string requestUri) { // Call the async method within a task to run it synchronously return Task.Run(() =&

我的公司有一个他们编写的Nuget软件包,可以轻松地为您完成各种常见任务。其中之一就是发出HTTP请求。通常,我总是使我的HTTP请求异步,但是在这个Nuget包中有以下代码:

    protected T GetRequest<T>(string requestUri)
    {
        // Call the async method within a task to run it synchronously
        return Task.Run(() => GetRequestAsync<T>(requestUri)).Result;
    }
protectedt GetRequest(字符串requestUri)
{
//在任务中调用异步方法以同步运行它
返回Task.Run(()=>GetRequestAsync(requestUri)).Result;
}
它调用此函数:

    protected async Task<T> GetRequestAsync<T>(string requestUri)
    {
        // Set up the uri and the client
        string uri = ParseUri(requestUri);
        var client = ConfigureClient();

        // Call the web api
        var response = await client.GetAsync(uri);

        // Process the response
        return await ProcessResponse<T>(response);
    }
受保护的异步任务GetRequestAsync(字符串requestUri)
{
//设置uri和客户端
字符串uri=ParseUri(requestUri);
var client=ConfigureClient();
//调用web api
var response=await client.GetAsync(uri);
//处理响应
返回等待处理响应(response);
}

我的问题是,通过将GetRequestAsync(requestUri)包装到Task.Run中并对返回的任务调用.Result,这段代码真的是同步运行的吗?这似乎是一个等待发生的死锁,我们在应用程序中发现了一些问题,这些问题在高负载运行时利用了这个功能

访问
Task.Result
将阻塞当前线程,直到
任务完成,因此它不是异步的


至于死锁,这不应该发生,因为
Task.Run
GetRequestAsync
使用另一个线程,而调用
Result
访问
任务不会阻止该线程。Result
将阻止当前线程,直到
任务完成,因此它不是异步的


至于死锁,这不应该发生,因为
Task.Run
GetRequestAsync
使用另一个线程,该线程不会被调用
Result

阻止,这不会导致死锁。但这肯定是一种资源浪费,因为其中一个线程可能会被阻塞

如果
GetRequest
如下所示,则可能出现死锁:

protected T GetRequest<T>(string requestUri)
{
    var task = GetRequestAsync<T>(requestUri);
    return task.Result;

    // or
    // return GetRequestAsync<T>(requestUri).Result;
}
protectedt GetRequest(字符串requestUri)
{
var task=GetRequestAsync(requestUri);
返回任务。结果;
//或
//返回GetRequestAsync(requestUri).Result;
}
在上面的示例中,您可以看到我在当前线程中调用
GetRequestAsync
。让我们给线程一个数字0。从<代码> GETRealestasycc>代码> ->代码>等待客户端。GetAsync(URI)< /代码>。code>.GetAsync
由线程1执行。在
.GetAsync
完成后,默认任务计划程序将尝试将执行流返回到执行该行的线程-返回到线程0。但是执行行(0)的线程现在被阻塞,因为在执行
GetRequestAsync()
之后,我们正在使用
task.Result
阻塞它(线程0)。因此,我们的线程0仍然被阻塞,因为它无法在
等待客户端之后继续执行
GetRequestAsync
。GetAsync(uri)
已完成,也无法给出结果


这是一个非常常见的错误,我想当被问及僵局时,你是指这个错误。您的代码没有导致它,因为您正在另一个线程中执行
GetRequestAsync

这不会导致死锁。但这肯定是一种资源浪费,因为其中一个线程可能会被阻塞

如果
GetRequest
如下所示,则可能出现死锁:

protected T GetRequest<T>(string requestUri)
{
    var task = GetRequestAsync<T>(requestUri);
    return task.Result;

    // or
    // return GetRequestAsync<T>(requestUri).Result;
}
protectedt GetRequest(字符串requestUri)
{
var task=GetRequestAsync(requestUri);
返回任务。结果;
//或
//返回GetRequestAsync(requestUri).Result;
}
在上面的示例中,您可以看到我在当前线程中调用
GetRequestAsync
。让我们给线程一个数字0。从<代码> GETRealestasycc>代码> ->代码>等待客户端。GetAsync(URI)< /代码>。code>.GetAsync
由线程1执行。在
.GetAsync
完成后,默认任务计划程序将尝试将执行流返回到执行该行的线程-返回到线程0。但是执行行(0)的线程现在被阻塞,因为在执行
GetRequestAsync()
之后,我们正在使用
task.Result
阻塞它(线程0)。因此,我们的线程0仍然被阻塞,因为它无法在
等待客户端之后继续执行
GetRequestAsync
。GetAsync(uri)
已完成,也无法给出结果


这是一个非常常见的错误,我想当被问及僵局时,你是指这个错误。您的代码没有导致它,因为您正在另一个线程中执行
GetRequestAsync

不会导致死锁的原因是Task.Run会将委托推送到线程池线程中执行。线程池线程没有SynchronizationContext,因此不会发生死锁,因为异步方法GetRequestAsync和调用方之间没有可锁定的同步上下文。与您可以直接在实际异步方法上以及在Task.Run()块中调用.Result相同,这也不会导致死锁

非常低效,但当你冻结CPU中的1个线程,即1个内核时,除了等待异步方法和其中的I/O调用完成,什么也不做。这可能就是为什么在高负载场景中会出现冻结


如果由于捕获同步上下文和异步调用时阻塞而出现同步/异步死锁问题,则无论单个调用的负载如何,死锁都会发生

不会导致死锁的原因是Task.Run会将委托推送到线程池线程中执行。线程池线程没有SynchronizationContext,因此不会发生死锁,因为异步方法GetRequestAsync和调用方之间没有可锁定的同步上下文。与您可以直接在实际异步方法上以及在Task.Run()块中调用.Result相同,这不会导致死锁