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

posted @ 2021-01-06 00:47  谷樵  阅读(140)  评论(0编辑  收藏  举报