EF Core DbContext 线程安全

前言

.Net Core WebApi Redis消息订阅_菜鸟Coco的博客-CSDN博客

后续

好不容易处理好后台任务,结果会时不时报错:

A second operation started on this context before a previous asynchronous operation completed.Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

百度了一圈,说把Startup.cs中AddDbContext 的生命周期改为Transient,改了之后:

 还是报类似的错误,

An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point. This can happen if a second operation is started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

        大致意思是  在配置上下文时尝试使用上下文。 DbContext 实例不能在 OnConfiguring 中使用,因为此时它仍在配置中。 如果在前一个操作完成之前对此上下文启动了第二个操作,则可能会发生这种情况。 不保证任何实例成员都是线程安全的。

        也就是说,DbContext 不是线程安全的,在 DbContext 执行 AcceptAllChanges 之前,会检测实体状态的改变,所以,SaveChanges 会和当前上下文一一对应,如果是同步方法,所有的操作都是等待,这是没有什么问题的,但试想一下,如果是异步多线程,当一个线程创建 DbContext 对象,然后进行一些实体状态修改,在还没有 AcceptAllChanges 执行之前,另一个线程也进行了同样的操作,虽然第一个线程可以 SaveChanges 成功,但是第二个线程肯定会报错,因为实体状态已经被另外一个线程中的 DbContext 应用了。具体看下面链接中大佬的分析,

EntityFramework DbContext 线程安全 - 田园里的蟋蟀 - 博客园

        因为我这个业务功能已经走到这儿了,没办法,只能想办法解决,最后只能来硬的了,加锁!!

        我是需要异步处理redis收到的订阅消息并存储到数据库,所以就在消息处理方法中加了锁,部分代码如下:

 public async Task DoWork(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                //等待10s执行某个任务
                await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
                //Thread.Sleep(30000);
                _helper.Unsubscribe(ChannelTest);
                //订阅通道,处理收到的消息
                await _helper.SubscribeAsync(ChannelTest, (channel, msg) => HandMsg(channel, msg));
            }
        }
        object lockObj = new object();

public void HandMsg(string channel, string msg)
        {
            lock (lockObj)
            {
                _logger.LogInformation("收到订阅数据:" + msg);

                MeterReadResult readResult = JsonConvert.DeserializeObject<MeterReadResult>(msg);
                // 自己的消息处理逻辑,使用DbContext操作数据库保存数据
    }
}

加锁之后,经测试,可以正常走流程没报错,问题暂时得到解决。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值