博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
TransactionScope线程安全问题整理
阅读量:4287 次
发布时间:2019-05-27

本文共 2457 字,大约阅读时间需要 8 分钟。

一、关于TransactionScope

1.在使用事务操作是,当程序中存在多个EF上下文,很多时候都是使用TransactionScope

2.TransactionScope使用简单,同一个事务中多个数据库上下文不会出现程序死锁

实验证明:

1.多线程中不同的事务锁定了相同的表会抛出异常
2.抛出异常的线程将自动结束
3.如果出现数据库表死锁,本程序的其他线程中如果也有关于锁定表的操作同样的会暂停等待一段时间
4.使用TransactionScope,锁定表的表同样会影响其他进程或者线程的查询操作,其他进程的查询样会挂起等待

二、关于使用TransactionScope多线程问题解决方案

解决方案1(推荐)

  1.所有的事务使用同一个线程锁,也就是说多个线程中事务操作开启同一个时间只有一个
  2.这种处理方式简单,不会抛出异常,但会稍微有点影响性能,因为一个事务开启的话,其他的事务操作会挂起等待
  3.特别说明,如果已知某些事务操作会执行比较长的时间,这种方式就不太合适了

代码实例:

使用封装过的TransactionScope,此处使用公共的排它锁,控制事务实例的创建和释放之间的代码,同一时间只有一个在运行。

也就是说只有一个事务释放之后,才会创建另一个事务。

/// /// 自定义事务处理,/// 此版本,数据库上下文会出现多个,所以事务使用 TransactionScope /// 使用排它锁,确保事务的单线程执行/// public class EFTransaction : IDisposable{    private readonly static object _MyLock = new object();    ///     /// 当前事务对象    ///     private TransactionScope tran = null;    public EFTransaction()    {        Monitor.Enter(_MyLock);//获取排它锁        this.tran = new TransactionScope();    }    ///     /// 提交    ///     public void Commit()    {        tran.Complete();    }    ///     /// 混滚操作,在Dispose(),中自动调用回滚    ///     public void Rollback()    {        //提前执行释放,回滚        if (tran != null)            tran.Dispose();    }    public void Dispose()    {        if (tran != null)            tran.Dispose();        Monitor.Exit(_MyLock);//释放排它锁    }}
解决方案2
  1.写代码的时候特别注意,不同事务之间不能操作相同的表,也就是多个事务中锁定的表不能有重合,否则就会抛出死锁异常
  2.这种方式对编码有要求,并且控制上相对比较难
  3.这种方式性能比较高,因为在逻辑上排除了数据库表死锁

解决方案3(推荐)

  1.在所有的事务中都退死锁异常进行处理,也就是使用try/catch 处理回滚,本次操作失败,下次在重新执行此业务逻辑
  2.这种方式比较折中,而且还可以在此一并处理其他的异常
  3.如果使用这种方式的话,开启事务参数,最好指定等待时间

代码实例:

此方案,一个事务操作一个锁,如果抛出死锁异常,本次操作失败,依赖下一次操作。

//对于锁推荐使用静态私有静态变量private readonly static object _MyLock = new object();/// /// 事务, 多表修改/// /// /// 
public bool UpdateName(string name){ lock (_MyLock) { using (var tran = new TransactionScope()) { try { ModuleOperate _module = new ModuleOperate(); //1.修改模块名称 _module.UpdateFirstName("模块:" + name); System.Threading.Thread.Sleep(100); //2.修改菜单 this.UpdateFirstName("菜单:" + name); //提交事务 tran.Complete(); } catch (Exception ex) { //使用TransactionScope,如果没有提交自动处理 //执行回滚操作处理 Console.WriteLine("当前操作Menu"); Console.WriteLine(ex.InnerException == null ? ex.Message : ex.InnerException.Message); } } } return true;}

更多参考:

转载地址:http://jnogi.baihongyu.com/

你可能感兴趣的文章
IE与火狐关于获取按键不兼容处理
查看>>
【跟我学Apache Commons】【一】综述
查看>>
【跟我学Apache Commons】【二】Lang&Math
查看>>
Eclipse文本高亮
查看>>
Junit4学习教程
查看>>
[Java]各种日志详细总结
查看>>
Maven-入门篇
查看>>
Jackson介绍(1)-jackson2.x与Jackson1.9的比较
查看>>
Nginx反向代理Tomcat
查看>>
java io-1基本概念
查看>>
【跟我学apache-commons】【四】commons-io的使用
查看>>
【HttpClient4.5中文教程】一.HttpClient简介与HTTP简介
查看>>
【HttpClient4.5中文教程】二.构建开发环境
查看>>
【HttpClient4.5中文教程】三.HttpClient执行基本请求
查看>>
【HttpClient4.5实训】一.HttpClient4.5模拟浏览器GET请求访问新浪网(非原文教程)
查看>>
【HttpClient4.5中文教程】四. HttpClient接口简介
查看>>
【HttpClient4.5中文教程】五. HttpClient执行上下文HttpContext
查看>>
【Mybatis3学习入门】【一】从JDBC到Mybatis
查看>>
【Mybatis3学习入门】【二】Mybatis快速入门
查看>>
JDBC进化史--从JDBC1.0到JDBC4.2
查看>>