一、在使用EF的TransactionScope事务时,如果多线程程序,经常会抛出如下异常
{"事务(进程 ID 58)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。"}
同一个TransactionScope逻辑操作事务在多线程中启动时会抛出异常。
解决方案:
使用线程锁,对同一个事务操作,仅允许一个线程执行
示例说明
1.出现异常的代码
事务操作定义
-
using (var tran = new TransactionScope())
-
{
-
ModuleOperate _module = new ModuleOperate();
-
//1.修改模块名称
-
_module.UpdateFirstName("模块:" + name);
-
//2.修改菜单
-
this.UpdateFirstName("菜单:" + name);
-
//提交事务
-
tran.Complete();
-
}
多线程调用定义
-
Action<object> update1 = (number) =>
-
{
-
while (true)
-
{
-
//将上线文实例放在本线程中创建
-
MenuOperate _menu = new MenuOperate();
-
_menu.UpdateName(Count.ToString());
-
-
Console.WriteLine("-------");
-
Console.WriteLine(_menu.GetName2());
-
-
Count++;
-
Thread.Sleep(1000 * Convert.ToInt32(number));
-
}
-
};
-
for (int i = 0; i < 3; i++)
-
{
-
Task.Factory.StartNew(update1, i + 1);
-
}
2.解决方案代码一:使用lock锁定
-
//对于锁推荐使用静态私有静态变量
-
private readonly static object _MyLock = new object();
-
/// <summary>
-
/// 事务, 多表修改
-
/// </summary>
-
/// <param name="name"></param>
-
/// <returns></returns>
-
public bool UpdateName(string name)
-
{
-
lock (_MyLock)
-
{
-
using (var tran = new TransactionScope())
-
{
-
ModuleOperate _module = new ModuleOperate();
-
1.修改模块名称
-
_module.UpdateFirstName("模块:" + name);
-
2.修改菜单
-
this.UpdateFirstName("菜单:" + name);
-
提交事务
-
tran.Complete();
-
}
-
}
-
return true;
-
}
3.解决方案代码二:使用Monitor封装TransactionScope
使用代码:
-
using (var tran = new EFTransaction())
-
{
-
//修改名称
-
name = ">>ModuleOperate:" + name;
-
UpdateFirstName(name);
-
-
//2.修改菜单
-
MenuOperate _menu = new MenuOperate();
-
_menu.UpdateFirstName(name);
-
-
//提交事务
-
tran.Commit();
-
}
EFTransaction类定义:
-
/// <summary>
-
/// 自定义事务处理,
-
/// 此版本,数据库上下文会出现多个,所以事务使用 TransactionScope
-
/// 使用排它锁,确保事务的单线程执行
-
/// </summary>
-
public class EFTransaction : IDisposable
-
{
-
private readonly static object _MyLock = new object();
-
/// <summary>
-
/// 当前事务对象
-
/// </summary>
-
private TransactionScope tran = null;
-
public EFTransaction()
-
{
-
Monitor.Enter(_MyLock);//获取排它锁
-
this.tran = new TransactionScope();
-
}
-
/// <summary>
-
/// 提交
-
/// </summary>
-
public void Commit()
-
{
-
tran.Complete();
-
}
-
/// <summary>
-
/// 混滚操作,在Dispose(),中自动调用回滚
-
/// </summary>
-
public void Rollback()
-
{
-
//提前执行释放,回滚
-
if (tran != null)
-
tran.Dispose();
-
}
-
public void Dispose()
-
{
-
if (tran != null)
-
tran.Dispose();
-
Monitor.Exit(_MyLock);//释放排它锁
-
}
-
}
使用验证代码:不同线程对于同一个事务操作多个事务实例,在当前程序中事务操作代码顺序同步执行,不会出现异常和数据异常。
-
Action<object> update1 = (number) =>
-
{
-
while (true)
-
{
-
//同一个线程使用多个事务
-
MenuOperate _menu = new MenuOperate();
-
ModuleOperate _module = new ModuleOperate();
-
-
事务操作一
-
_menu.UpdateName(Count.ToString());
-
Thread.Sleep(Count); //错开等待时间,测试多线程异步问题
-
//事务操作二
-
_module.UpdateName(Count.ToString());
-
Console.WriteLine("-------");
-
Console.WriteLine(_menu.GetName2());
-
-
Count++;
-
Thread.Sleep(1000 * Convert.ToInt32(number));
-
}
-
};
-
for (int i = 0; i < 3; i++)
-
{
-
Task.Factory.StartNew(update1, i + 1);
-
}
原文引自:https://blog.csdn.net/u011127019/article/details/54576873