巧用ThreadPool.QueueUserWorkItem实现接口二次调用

背景

最近做了个项目有个接口涉及到批量计算的问题,耗时比较长。大家都知道,接口等待时间太长肯定是不可取的。那么只能做异步处理了;但是问题来了这个项目没有什么消息队列、redis之类的使用,本着怎么简单怎么来的思路,新搞个消息队列不现实;这时候,多线程派上用场。.

再次遇到问题

于是噼里啪啦写了一顿,发现有个问题,我的方法涉及到很多ef core  数据库操作,在多线程的条件下,

报错如下:System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913

生命周期为Scope方式,随着请求的结束,实例生命周期也会被释放,因此在多线程下若共享实例,容易出现实例已释放的错误,报错如下:Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.Autofac   at Autofac.Core.Lifetime.LifetimeScope.BeginLifetimeScope(Object tag)。通过注入IServiceProvider,

就是这种方式也是不行的,还是需要改到原来的代码,还是违背初衷。

峰回路转

本来一筹莫展的,突然想到既然接口一次太慢了,那就分2次执行,第二次可以使用多线程触发,自己调用自己的耗时接口,这样就不需要改到原来的底层逻辑,要做的仅仅是把自己的方法拆分成2个。

1、请求一个异步方法,然后接口直接返回

  /// <summary>
        /// 批量  添加  一级指标 数据(74.22分,全省第10名)
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("adminaddAreadatabatch")]
        public bool addAreadatabatch(List<AreadataDto> entity)
        {
            //  foreach (var item in entity ?? new List<AreadataDto>())
            //  {
            //   try
            //   {
            _chartBll.Addbat(entity);
            //  }
            //   catch (Exception ex)
            //  {
            //     _logger.Error(ex);
            //  }
            //  }
`
            ThreadPool.QueueUserWorkItem(new WaitCallback(InsertNewsInfoExt), JsonConvert.SerializeObject(entity));`

            return true;
        }

2、这里做一个http请求

    private void InsertNewsInfoExt(object info)
        {
            var client = new RestClient("http://xxxx/api/ningdeChart/updateAreadata");
            client.Timeout = -1;
            var request = new RestRequest(Method.POST);
            request.AddHeader("Content-Type", "application/json");
            var body = info.ToString();
            request.AddParameter("application/json", body, ParameterType.RequestBody);
            IRestResponse response = client.Execute(request);
            Console.WriteLine(response.Content);
        }

    

3、在原来的接口adminaddAreadatabatch做下二次拆分,提供一个新的api

    [HttpPost]
        [Route("updateAreadata")]
        public bool updateAreadata(List<AreadataDto> entity)
        {
            foreach (var item in entity ?? new List<AreadataDto>())
            {
                try
                {
                    _logger.Info("updateAreadata");
                    _chartBll.updateAreadata(item.areaid, item.t1);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            }
            return true;
        }

问题得到解决。