EntityFramework多线程安全和连接池问题探究

这篇具有很好参考价值的文章主要介绍了EntityFramework多线程安全和连接池问题探究。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、多线程操作数据库出现的问题

因为EntityFramework的DataContext不是线程安全的,所有多线程使用EntityFramework的DataContext遇到了下面错误

EntityFramework多线程安全和连接池问题探究

“A second operation was started on this context 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.”

即使使用原生的MySqlConnection对象,多线程访问同样会出现问题
测试代码

using DependencyInjection;
using EntityDbService.Models;
using EntityDbService.Table;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Share.SystemParameter;

namespace DataBaseTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var serviceProvider = new ServiceCollection()
                    .AddDbContext<DataContext>(optionsBuilder => optionsBuilder.UseMySql(Parameters.GetConnectionString(), new MySqlServerVersion(new Version(5, 7, 0))))
                    .BuildServiceProvider();
            SingleService.SetServiceProvider(serviceProvider);
            StartEntityTest();
        }

        private static List<Thread> _threads = new List<Thread>();
        private static bool _isStopThread = false;

        public static void StartEntityTest()
        {
            Thread addThread1 = new Thread(AddThread);
            _threads.Add(addThread1);
            addThread1.Start();

            Thread updateThread1 = new Thread(UpdateThread);
            _threads.Add(updateThread1);
            updateThread1.Start();

            Thread selectThread1 = new Thread(SelectThread);
            _threads.Add(selectThread1);
            selectThread1.Start();
        }

        public static void AddThread()
        {
            var dataContext = SingleService.Services.GetService<DataContext>();
            while (!_isStopThread)
            {
                try
                {
                    EquipmentTable equipmentTable = new EquipmentTable();
                    equipmentTable.Name = "test";
                    equipmentTable.ImeiStr = "123456789";
                    equipmentTable.Timestamp = DateTime.Now;
                    equipmentTable.CompanyId = 0;
                    dataContext.EquipmentTables.Add(equipmentTable);
                    var count = dataContext.SaveChanges();
                    Console.WriteLine($"AddThread:{Thread.GetCurrentProcessorId},equipmentTable.SaveChanges():{count}");
                    Thread.Sleep(100);
                }
                catch(Exception ex)
                {
                    Console.WriteLine($"AddThread:{Thread.GetCurrentProcessorId},Exception:{ex.ToString()}");
                    Thread.Sleep(1000);
                }
            }
        }

        public static void UpdateThread()
        {
            var dataContext = SingleService.Services.GetService<DataContext>();
            while (!_isStopThread)
            {
                try
                {
                    Random random = new Random();
                    var id = random.Next(1, 10000);
                    var equipmentTable = dataContext.EquipmentTables.Find(id);
                    if (equipmentTable != null)
                    {
                        equipmentTable.CompanyId = equipmentTable.CompanyId + 1;
                        var count = dataContext.SaveChanges();
                        Console.WriteLine($"UpdateThread:{Thread.GetCurrentProcessorId},equipmentTable.SaveChanges():{count}");
                    }
                    else
                    {
                        Console.WriteLine($"UpdateThread:{Thread.GetCurrentProcessorId},equipmentTable.SaveChanges():{0}");
                    }
                    Thread.Sleep(100);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"UpdateThread:{Thread.GetCurrentProcessorId},Exception:{ex.ToString()}");
                    Thread.Sleep(1000);
                }
            }
        }

        public static void SelectThread()
        {
            var dataContext = SingleService.Services.GetService<DataContext>();
            while (!_isStopThread)
            {
                try
                {
                    Random random = new Random();
                    var id = random.Next(1, 10000);
                    var equipmentTable = dataContext.EquipmentTables.Skip(id).Take(id * 10);
                    if (equipmentTable != null && equipmentTable.Count() > 0)
                    {
                        Console.WriteLine($"SelectThread:{Thread.GetCurrentProcessorId},equipmentTable.Count():{equipmentTable.Count()}");
                    }
                    else
                    {
                        Console.WriteLine($"SelectThread:{Thread.GetCurrentProcessorId},equipmentTable.Count():{0}");
                    }
                    Thread.Sleep(100);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"SelectThread:{Thread.GetCurrentProcessorId},Exception:{ex.ToString()}");
                    Thread.Sleep(1000);
                }
            }
        }
    }
}

二、第一个想法,为每个数据表创建一个DataContext,然后加锁

创建多个DataContext

.AddDbContext<DataContext>(optionsBuilder => optionsBuilder.UseMySql(ConnectionString, new MySqlServerVersion(new Version(5, 7, 0))))
.AddDbContext<EquipmentContext>(optionsBuilder => optionsBuilder.UseMySql(ConnectionString, new MySqlServerVersion(new Version(5, 7, 0))))

EntityFramework多线程安全和连接池问题探究

很显然是不可行的

PM> Add-Migration init
Build started...
Build succeeded.
More than one DbContext was found. Specify which one to use. Use the '-Context' parameter for PowerShell commands and the '--context' parameter for dotnet commands.

三、第二个想法,全局锁,很显然可以用,但是性能不行

测试代码

using EntityDbService.Table;
using Microsoft.EntityFrameworkCore;

namespace EntityDbService.Models
{
    public class DataContext : DbContext
    {

        public DataContext(DbContextOptions<DataContext> options)
            : base(options)
        {

            if (Database.GetPendingMigrations().Any())
            {
                Database.Migrate(); //执行迁移
            }
        }

        public DbSet<EquipmentTable> EquipmentTables { get; set; }
        public object Lock { get => _sign; set => _sign = value; }
        //锁
        private object _sign = new object();
    }
}

使用时要全部加锁

using DependencyInjection;
using EntityDbService.Models;
using EntityDbService.Table;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataBaseTest
{
    public static class EntityTest
    {
        private static List<Thread> _threads = new List<Thread>();
        private static bool _isStopThread = false;
        private static int _time = 10;

        public static void StartEntityTest()
        {
            Thread addThread1 = new Thread(AddThread);
            _threads.Add(addThread1);
            addThread1.Start();

            Thread addThread2 = new Thread(AddThread);
            _threads.Add(addThread2);
            addThread2.Start();

            Thread updateThread1 = new Thread(UpdateThread);
            _threads.Add(updateThread1);
            updateThread1.Start();

            Thread updateThread2 = new Thread(UpdateThread);
            _threads.Add(updateThread2);
            updateThread2.Start();

            Thread selectThread1 = new Thread(SelectThread);
            _threads.Add(selectThread1);
            selectThread1.Start();

            Thread selectThread2 = new Thread(SelectThread);
            _threads.Add(selectThread2);
            selectThread2.Start();
        }

        public static void AddThread()
        {
            var dataContext = SingleService.Services.GetService<DataContext>();
            while (!_isStopThread)
            {
                try
                {
                    int count = 0;
                    lock (dataContext.Lock)
                    {
                        EquipmentTable equipmentTable = new EquipmentTable();
                        equipmentTable.Name = "test";
                        equipmentTable.ImeiStr = "123456789";
                        equipmentTable.Timestamp = DateTime.Now;
                        equipmentTable.CompanyId = 0;
                        dataContext.EquipmentTables.Add(equipmentTable);
                        count = dataContext.SaveChanges();
                    }
                    Console.WriteLine($"AddThread:{Thread.GetCurrentProcessorId()},equipmentTable.SaveChanges():{count}");
                    Thread.Sleep(_time);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"AddThread:{Thread.GetCurrentProcessorId()},Exception:{ex.ToString()}");
                    Thread.Sleep(1000);
                }
            }
        }

        public static void UpdateThread()
        {
            var dataContext = SingleService.Services.GetService<DataContext>();
            while (!_isStopThread)
            {
                try
                {
                    Random random = new Random();
                    var id = random.Next(1, 10000);
                    var count = 0;
                    lock (dataContext.Lock)
                    {
                        EquipmentTable equipmentTable = dataContext.EquipmentTables.Find(id);
                        if (equipmentTable != null)
                        {
                            equipmentTable.CompanyId = equipmentTable.CompanyId + 1;
                            count = dataContext.SaveChanges();
                        }
                    }
                    Console.WriteLine($"UpdateThread:{Thread.GetCurrentProcessorId()},equipmentTable.SaveChanges():{count}");
                    Thread.Sleep(_time);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"UpdateThread:{Thread.GetCurrentProcessorId()},Exception:{ex.ToString()}");
                    Thread.Sleep(1000);
                }
            }
        }

        public static void SelectThread()
        {
            var dataContext = SingleService.Services.GetService<DataContext>();
            while (!_isStopThread)
            {
                try
                {
                    Random random = new Random();
                    var id = random.Next(1, 10000);
                    lock (dataContext.Lock)
                    {
                        var equipmentTable = dataContext.EquipmentTables.Skip(id).Take(10);
                   
                        if (equipmentTable != null && equipmentTable.Count() > 0)
                        {
                            Console.WriteLine($"SelectThread:{Thread.GetCurrentProcessorId()},equipmentTable.Count():{equipmentTable.Count()}");
                        }
                        else
                        {
                            Console.WriteLine($"SelectThread:{Thread.GetCurrentProcessorId()},equipmentTable.Count():{0}");
                        }
                    }
                    Thread.Sleep(_time);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"SelectThread:{Thread.GetCurrentProcessorId()},Exception:{ex.ToString()}");
                    Thread.Sleep(1000);
                }
            }
        }
    }
}

四、第三个想法,建立一个数据库连接池,微软已经实现,只需要配置即可

池中每个实例都是一个独立的数据库连接,每次要使用时都去池中请求空闲连接,因为连接多所以性能较单个数据库连接加锁要好很多,如果都没有空闲,可以通过机制进行等待或者增加连接池中连接。

sql设置

启用连接池

string connStringUsePool = "server=.;database=master;uid=sa;pwd=123;pooling=true;connection lifetime=0;min pool size = 1;max pool size=500";

mysql设置

启用连接池

string connStringUsePool = "server=127.0.0.1;uid=root;pwd=xxx;database=databasetest;pooling=true;connectionlifetime=5;minpoolsize=1;maxpoolsize=500;"

DataContext代码

using EntityDbService.Table;
using Microsoft.EntityFrameworkCore;
using Share.SystemParameter;

namespace EntityDbService.Models
{
    public class DataContext : DbContext
    {
        public void Migrate()
        {
            if (Database.GetPendingMigrations().Any())
            {
                Database.Migrate(); //执行迁移
            }
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseMySql(Parameters.GetConnectionString(), new MySqlServerVersion(new Version(5, 7, 0)));
        }

        public DbSet<EquipmentTable> EquipmentTables { get; set; }
    }
}

EquipmentTable类代码

public class EquipmentTable
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public int GroupId { get; set; }
    public int CompanyId { get; set; }
    public string Name { get; set; } = String.Empty;
}

SchoolContextFactory类代码,用来生成脚本的

namespace EntityDbService.Models
{
    public class SchoolContextFactory : IDesignTimeDbContextFactory<DataContext>
    {
        // IConfiguration Configuration { get; } //使用Configuration 获取不到GetConnectionString("SchoolContext")。不能用
        public DataContext CreateDbContext(string[] args)
        {
            //var optionsBuilder = new DbContextOptionsBuilder<DataContext>();
            //string ConnectionString = Parameters.GetConnectionString();
            //optionsBuilder.UseMySql(ConnectionString, new MySqlServerVersion(new Version(5, 7, 0)));
            //return new DataContext(optionsBuilder.Options);
            return new DataContext();
        }
    }
}

使用,如果并发过高会抛出Too many connections太多的连接文章来源地址https://www.toymoban.com/news/detail-440675.html

try
{
    using (DataContext dataContext = new DataContext())
    {
        EquipmentTable equipmentTable = new EquipmentTable();
        equipmentTable.Name = "test";
        equipmentTable.ImeiStr = "123456789";
        equipmentTable.CompanyId = 0;
        dataContext.EquipmentTables.Add(equipmentTable);
        dataContext.SaveChanges();
    }
}
catch (Exception ex)
{
    Console.WriteLine($"AddThread:{Thread.GetCurrentProcessorId()},Exception:{ex.ToString()}");
    Thread.Sleep(1000);
}

到了这里,关于EntityFramework多线程安全和连接池问题探究的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【多线程】线程安全问题

    我们先来看一段代码: 这段代码是对 count 自增 10w 次,随之的打印结果 count = 100000,相信也没有任何的歧义,那么上述代码是否能优化呢?能否让速度更快呢? 相信学习到这里大家都会想到用多线程,可以搞两个线程,每个线程执行 5w 次自增就行了,甚至还可以搞五个线程

    2024年02月01日
    浏览(32)
  • java基础之线程安全问题以及线程安全集合类

    当多个线程同时访问同一个临界资源时,原子操作可能被破坏,会导致数据丢失, 就会触发线程安全问题 临界资源: 被多个线程同时访问的对象 原子操作: 线程访问临界资源的过程中不可更改和缺失的操作 互斥锁 每个对象都默认拥有互斥锁, 该锁默认不开启. 当开启互斥锁之后

    2024年01月18日
    浏览(52)
  • Java多线程之线程安全问题

    我们知道操作系统中线程程的调度是抢占式执行的, 宏观上上的感知是随机的, 这就导致了多线程在进行线程调度时线程的执行顺序是不确定的, 因此多线程情况下的代码的执行顺序可能就会有无数种, 我们需要保证这无数种线程调度顺序的情况下, 代码的执行结果都是正确的

    2023年04月17日
    浏览(45)
  • Java中的多线程——线程安全问题

    作者:~小明学编程   文章专栏:JavaEE 格言:热爱编程的,终将被编程所厚爱。 目录 多线程所带来的不安全问题 什么是线程安全 线程不安全的原因 修改共享数据 修改操作不是原子的 内存可见性对线程的影响 指令重排序 解决线程不安全的问题 synchronized 互斥 刷新内

    2024年02月03日
    浏览(78)
  • 【多线程】线程安全问题原因与解决方案

    目录 线程安全的概念 线程不安全示例 线程不安全的原因      多个线程修改了同一个变量     线程是抢占式执行的     原子性     内存可见性     有序性 线程不安全解决办法  synchronized -监视器锁monitor lock     synchronized 的特性         互斥         刷新内

    2024年02月06日
    浏览(37)
  • java 线程安全问题 三种线程同步方案 线程通信(了解)

    线程安全问题指的是,多个线程同时操作同一个共享资源的时候,可能会出现业务安全问题。 下面代码演示上述问题,先定义一个共享的账户类: 在定义一个取钱的线程类 最后,再写一个测试类,在测试类中创建两个线程对象 某个执行结果: 为了解决前面的线程安全问题,

    2024年02月09日
    浏览(43)
  • 【Java】线程安全问题

    在之前的文章中,已经介绍了关于线程的基础知识。 我的主页: 🍆🍆🍆爱吃南瓜的北瓜 欢迎各位大佬来到我的主页进行指点 一同进步!!! 我们创建两个线程t1和t2,对静态变量count执行++操作各50000次。 我们的预期结果是100000。但是当两个线程分别执行++操作时最后的结果

    2024年04月10日
    浏览(43)
  • 线程安全问题及解决方法

    线程在执行的过程中出现错误的主要原因有以下几种: 1、根本原因 导致线程不安全的所有原因中,最根本的原因是——抢占式执行。因为CPU字在进行线程调度的时候,是随机调度的,而且这是无法避免的一种原因。 2、代码结构 当多个线程同时修改同一个变量的时候,很容

    2024年02月06日
    浏览(34)
  • 线程安全问题

    目录 🐇今日良言:一路惊喜 马声蹄蹄 🐼一、线程安全问题 🐳1.概念 🐳2.代码 🐳3.原因 🐳4.解决方案 🐳 1.概念 如果多线程环境下代码运行的结果是符合我们预期的,即该代码在单线程中运行得到的结果,那么就说说这个程序是线程安全的,否则就是线程不安全的. 线程安全问

    2024年02月15日
    浏览(32)
  • 【JavaEE】多线程之线程安全(synchronized篇),死锁问题

    线程安全问题 观察线程不安全 线程安全问题的原因  从原子性入手解决线程安全问题 ——synchronized synchronized的使用方法  synchronized的互斥性和可重入性 死锁 死锁的三个典型情况  死锁的四个必要条件  破除死锁 在前面的章节中,我们也了解到多线程为我们的程序带来了

    2024年02月01日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包