10

根据我所阅读的内容asp.net core,已经删除了同步上下文。这意味着在await调用之后执行代码的线程可能与之前执行代码的线程不同await

那么在方法HttpContext中使用仍然安全吗?async还是可以在await通话后获得不同的上下文?

例如在控制器动作中

public async Task<IActionResult> Index()
{
    var context1 = HttpContext;
    await Task.Delay(1000);
    var context2 = HttpContext;
    ....
}

context1 可以与 context2 不同吗?

在无控制器方法中获取上下文的推荐方法是依赖注入IHttpContextAccessor

对模式IHttpContextAccessor.HttpContext安全吗?async await

IE 可以 context1 与 context2 不同吗?

public async void Foo(IHttpContextAccessor accessor)
{
    var context1 = accessor.HttpContext;
    await Task.Delay(1000);
    var context2 = accessor.HttpContext;
}
4

2 回答 2

14

那么在异步方法中使用 HttpContext 是否仍然安全?或者是否有可能在等待调用后获得不同的上下文?

async和和 ASP.NET pre-Core的全部问题HttpContext是由于代码通常HttpContext来自HttpContext.Current. ASP.NET 是一个多线程服务器,每个服务器都await可以在不同的线程上恢复。因此,在异步代码恢复之前,ASP.NET pre-Core 必须有一个AspNetSynchronizationContext托管设置。HttpContext.Current

现代 ASP.NET Core没有同步上下文。但这很好,因为它没有HttpContext.Current. 获取实例的唯一方法HttpContext是通过本地属性(例如,HttpContext在您的控制器类上)或依赖注入 ( IHttpContextAccessor)。

(学究式注释:上面的解释有点简化了——ASP.NET 预核心同步上下文确实处理了其他事情HttpContext.Current——但同样的总体解释适用于它的所有其他职责——即,它们在核心世界中不是必需的)

因此,上下文不可能不同。它们是相同的属性 - 相同的对象实例。ASP.NET pre-Core 的问题是静态属性值HttpContext.Current,它已在 ASP.NET Core 中删除。

于 2019-09-12T14:26:01.847 回答
1

根据文档:https ://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-5.0#do-not-access-httpcontext-from-multiple-threads

HttpContext 不是线程安全的。从多个线程并行访问 HttpContext 可能会导致未定义的行为,例如挂起、崩溃和数据损坏。

不要这样做:以下示例发出三个并行请求,并在传出 HTTP 请求之前和之后记录传入请求路径。请求路径是从多个线程访问的,可能是并行的。

public class AsyncBadSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
       var query1 = SearchAsync(SearchEngine.Google, query);
       var query2 = SearchAsync(SearchEngine.Bing, query);
       var query3 = SearchAsync(SearchEngine.DuckDuckGo, query);

       await Task.WhenAll(query1, query2, query3);

       var results1 = await query1;
       var results2 = await query2;
       var results3 = await query3;

       return SearchResults.Combine(results1, results2, results3);
    }       

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.", 
                                    HttpContext.Request.Path);
            searchResults = _searchService.Search(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", 
                                    HttpContext.Request.Path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", 
                             HttpContext.Request.Path);
        }

        return await searchResults;
    }
}

执行此操作:以下示例在发出三个并行请求之前复制传入请求中的所有数据。

public class AsyncGoodSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        string path = HttpContext.Request.Path;
        var query1 = SearchAsync(SearchEngine.Google, query,
                             path);
        var query2 = SearchAsync(SearchEngine.Bing, query, path);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query, path);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }

       private async Task<SearchResults> SearchAsync(SearchEngine engine, string query,
                                                  string path)
       {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.",
                                   path);
            searchResults = await _searchService.SearchAsync(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", path);
        }

        return await searchResults;
    }
}
于 2020-03-20T18:42:25.763 回答