.net 核心在异步等待时的奇怪行为(可能是因为多个请求)

性感MF

我有以下用例FetchDataService(作为作用域注入)正在使用 httpClient 从 API 获取数据。
然后服务分析数据并将其运行await SaveAsync到数据库中。
到目前为止,一切都很好。
然后我添加了另一个服务:(AddressEnrichmentService作为作用域注入)在“分析”阶段异步调用外部服务并带来更多数据。
这项服务正在搞乱整个应用程序。我从指示一些线程问题的数据库处理程序 (NHibernate) 中得到随机异常。我真的不能把我的手指放在它上面。几个不同的随机异常......运行时会抛出这些异常await SaveAsync(我会在最后添加它们)。

public async Task Fetch(string url, UserModel userModel ){
    string res = null;
    using (var httpClient = new HttpClient()){
       res = await httpClient.GetStringAsync(url);
    }
    await databaseLogRepository.SaveAsync(new Log{Message = "The data is here..." + res});
    var profile = JsonConvert.DeserializeObject<ProfileModel >(res);

    profile.Data.Locations.ForEach(async x =>
    {
        var address = await addressEnrichmentService.EnrichAsync(x.StreetAddress + " " + x.Locality + " " + x.Country);
        userModel.Address = address;
        await databaseLogRepository.SaveAsync(new Log{Message = "enriched address"});
   });
}

有问题的服务:

public class AddressEnrichmentService : IAddressEnrichmentService
{
        public async Task<AddressModel> EnrichAsync(string address)
        {
            string res = null;
            using (HttpClient client = new HttpClient())
            {
                var url = "https://maps.googleapis.com/maps/api/geocode/json?key=__KEY__&address=" + HttpUtility.UrlEncode(address);
                    res = await client.GetStringAsync(url);// probably the problematic row
            }
             return JsonConvert.DeserializeObject<AddressModel>(res);
        }
 }

用法

UserModel userModel = new UserModel();
await fetchDataService.Fetch(url, userModel);
await userRepo.SaveAsync(userModel);

addressEnrichmentService.EnrichAsync是把一切都搞砸了。我怎么知道EnrichAsync是不是搞砸了?
如果我转换res = await client.GetStringAsync(url);res = client.GetStringAsync(url).GetAwaiter().GetResult();我不会收到任何错误。100 次尝试 100 次我没有错误。如果我回滚它,res = await client.GetStringAsync(url);我每次都会出错。

关于异常,再次,我得到一些与并发相关的错误的迹象,这是保存方法:

public async Task<T> SaveAsync(T entity)
{
    using (var tr = session.BeginTransaction())
    {
        //session is injected as scoped
        await session.SaveOrUpdateAsync(entity);
        tr.Commit();
    }
    return entity;
}

MySqlException: There is already an open DataReader associated with this Connection which must be closed first.

NHibernate.HibernateException: 'Flush during cascade is dangerous'

西奥多·祖利亚斯

这是问题所在:

profile.Data.Locations.ForEach(async x =>

ForEach方法接受一个Action,而不是一个异步委托。因此,您的async委托 (the Task)的返回值被忽略。结果是你最终得到了一种async void方法,它在你之前折磨了无数开发人员(搜索async void以查看永无止境的相关问题列表)。由于Task不等待 s,它们同时运行。多个线程正在向您的数据库发送并发请求,这显然无法处理并行请求。更不用说这些任务不能再被观察到了。它们已成为一劳永逸的任务。

为了解决这个问题ForEach,用普通香草代替花式foreach每个都Task将得到适当的等待,不会发生不需要的并行性,问题将得到解决。

foreach (var x in profile.Data.Locations)
{
    var address = await addressEnrichmentService.EnrichAsync(
        x.StreetAddress + " " + x.Locality + " " + x.Country);
    userModel.Address = address;
    await databaseLogRepository.SaveAsync(new Log{Message = "enriched address"});
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章