.NET 性能最佳做法:请勿从多个线程访问HttpContext

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;    }