• EF 多线程TransactionScope事务异常"事务(进程 ID 58)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。


    一、在使用EF的TransactionScope事务时,如果多线程程序,经常会抛出如下异常

    {"事务(进程 ID 58)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。"}

    同一个TransactionScope逻辑操作事务在多线程中启动时会抛出异常。

    解决方案:

    使用线程锁,对同一个事务操作,仅允许一个线程执行

    示例说明

    1.出现异常的代码

    事务操作定义

    1.  
      using (var tran = new TransactionScope())
    2.  
      {
    3.  
      ModuleOperate _module = new ModuleOperate();
    4.  
      //1.修改模块名称
    5.  
      _module.UpdateFirstName("模块:" + name);
    6.  
      //2.修改菜单
    7.  
      this.UpdateFirstName("菜单:" + name);
    8.  
      //提交事务
    9.  
      tran.Complete();
    10.  
      }


    多线程调用定义

    1.  
      Action<object> update1 = (number) =>
    2.  
      {
    3.  
      while (true)
    4.  
      {
    5.  
      //将上线文实例放在本线程中创建
    6.  
      MenuOperate _menu = new MenuOperate();
    7.  
      _menu.UpdateName(Count.ToString());
    8.  
       
    9.  
      Console.WriteLine("-------");
    10.  
      Console.WriteLine(_menu.GetName2());
    11.  
       
    12.  
      Count++;
    13.  
      Thread.Sleep(1000 * Convert.ToInt32(number));
    14.  
      }
    15.  
      };
    16.  
      for (int i = 0; i < 3; i++)
    17.  
      {
    18.  
      Task.Factory.StartNew(update1, i + 1);
    19.  
      }


    2.解决方案代码一:使用lock锁定

    1.  
      //对于锁推荐使用静态私有静态变量
    2.  
      private readonly static object _MyLock = new object();
    3.  
      /// <summary>
    4.  
      /// 事务, 多表修改
    5.  
      /// </summary>
    6.  
      /// <param name="name"></param>
    7.  
      /// <returns></returns>
    8.  
      public bool UpdateName(string name)
    9.  
      {
    10.  
      lock (_MyLock)
    11.  
      {
    12.  
      using (var tran = new TransactionScope())
    13.  
      {
    14.  
      ModuleOperate _module = new ModuleOperate();
    15.  
      1.修改模块名称
    16.  
      _module.UpdateFirstName("模块:" + name);
    17.  
      2.修改菜单
    18.  
      this.UpdateFirstName("菜单:" + name);
    19.  
      提交事务
    20.  
      tran.Complete();
    21.  
      }
    22.  
      }
    23.  
      return true;
    24.  
      }

    3.解决方案代码二:使用Monitor封装TransactionScope

    使用代码:

    1.  
      using (var tran = new EFTransaction())
    2.  
      {
    3.  
      //修改名称
    4.  
      name = ">>ModuleOperate:" + name;
    5.  
      UpdateFirstName(name);
    6.  
       
    7.  
      //2.修改菜单
    8.  
      MenuOperate _menu = new MenuOperate();
    9.  
      _menu.UpdateFirstName(name);
    10.  
       
    11.  
      //提交事务
    12.  
      tran.Commit();
    13.  
      }


    EFTransaction类定义:

    1.  
      /// <summary>
    2.  
      /// 自定义事务处理,
    3.  
      /// 此版本,数据库上下文会出现多个,所以事务使用 TransactionScope
    4.  
      /// 使用排它锁,确保事务的单线程执行
    5.  
      /// </summary>
    6.  
      public class EFTransaction : IDisposable
    7.  
      {
    8.  
      private readonly static object _MyLock = new object();
    9.  
      /// <summary>
    10.  
      /// 当前事务对象
    11.  
      /// </summary>
    12.  
      private TransactionScope tran = null;
    13.  
      public EFTransaction()
    14.  
      {
    15.  
      Monitor.Enter(_MyLock);//获取排它锁
    16.  
      this.tran = new TransactionScope();
    17.  
      }
    18.  
      /// <summary>
    19.  
      /// 提交
    20.  
      /// </summary>
    21.  
      public void Commit()
    22.  
      {
    23.  
      tran.Complete();
    24.  
      }
    25.  
      /// <summary>
    26.  
      /// 混滚操作,在Dispose(),中自动调用回滚
    27.  
      /// </summary>
    28.  
      public void Rollback()
    29.  
      {
    30.  
      //提前执行释放,回滚
    31.  
      if (tran != null)
    32.  
      tran.Dispose();
    33.  
      }
    34.  
      public void Dispose()
    35.  
      {
    36.  
      if (tran != null)
    37.  
      tran.Dispose();
    38.  
      Monitor.Exit(_MyLock);//释放排它锁
    39.  
      }
    40.  
      }

    使用验证代码:不同线程对于同一个事务操作多个事务实例,在当前程序中事务操作代码顺序同步执行,不会出现异常和数据异常。

      1.  
        Action<object> update1 = (number) =>
      2.  
        {
      3.  
        while (true)
      4.  
        {
      5.  
        //同一个线程使用多个事务
      6.  
        MenuOperate _menu = new MenuOperate();
      7.  
        ModuleOperate _module = new ModuleOperate();
      8.  
         
      9.  
        事务操作一
      10.  
        _menu.UpdateName(Count.ToString());
      11.  
        Thread.Sleep(Count); //错开等待时间,测试多线程异步问题
      12.  
        //事务操作二
      13.  
        _module.UpdateName(Count.ToString());
      14.  
        Console.WriteLine("-------");
      15.  
        Console.WriteLine(_menu.GetName2());
      16.  
         
      17.  
        Count++;
      18.  
        Thread.Sleep(1000 * Convert.ToInt32(number));
      19.  
        }
      20.  
        };
      21.  
        for (int i = 0; i < 3; i++)
      22.  
        {
      23.  
        Task.Factory.StartNew(update1, i + 1);
      24.  
        }

    原文引自:https://blog.csdn.net/u011127019/article/details/54576873

  • 相关阅读:
    团队冲刺-1
    最优惠购买书籍
    gogoing软件NABCD
    二维数组首尾相连
    首尾相连一维数组的最大子数组和
    二维数组返回最大子矩阵之和
    石家庄铁道大学基础教学楼电梯使用调查
    子数组最大值求和
    返回一个整数数组中最大子数组的和-课堂训练(子数组为连续)
    软件工程概论-四则运算
  • 原文地址:https://www.cnblogs.com/lxf1117/p/14238810.html
Copyright © 2020-2023  润新知