知识大全 详解.NET编程过程中的线程冲突

Posted 函数

篇首语:仰天大笑出门去,我辈岂是蓬蒿人。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 详解.NET编程过程中的线程冲突相关的知识,希望对你有一定的参考价值。

详解.NET编程过程中的线程冲突  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  一 什么是线程冲突

  线程冲突其实就是指 两个或以上的线程同时对同一个共享资源进行操作而造成的问题

  一个比较经典的例子是 用一个全局变量做计数器 然后开N个线程去完成某个任务 每个线程完成一次任务就将计数器加一 直到完成 次任务 如果不考虑线程冲突问题 用类似下面的代码去做 则很可能会超额完成任务 线程越多 完成任务次数超出 次的可能性就越大

  伪代码如下

  

  int count = ;//全局计数器

  void ThreadMethod()//运行在每个线程的方法

  

  while( true )

  

  if ( count >= )//如果达到任务指标

  break;//中断线程执行

  DoSomething();//完成某个任务

  count++;

  

  

  //省略线程的创建等代码

  具体的 为什么会超额完成任务的原因在这里我就不赘述了 这个例子在单线程环境中是绝对不会超额完成任务的

  当然 在这个例子中 将count++放到if语句中 也许能降低一些事故发生的概率 但那不是绝对的 换言之这样的程序不能杜绝超额完成任务的可能

  其实从线程冲突的定义中我们不难发现 要造成线程冲突有两个必要条件 多线程和共享资源 这两个条件中有一个不成立 就不可能发生线程冲突问题

  所以 在单线程环境中 是不存在线程冲突的问题的 不过很可惜的是 我们的软件早已进化到了多进程多线程的时代 单线程的程序几乎是不存在的 无论是WinForm还是WebForm 程序运行的环境都是多线程的 而不论你自己是不是明确的开启了一个线程

  既然多线程是不可避免的 那么要避免线程冲突就只能从共享资源来开刀了

  二 线程安全的资源

  如果大家经常看MSDN或者VS帮助中的 NET类库参考的话 就不难发现几乎所有的类型都有这么一句话的描述 此类型的任何公共 static(在 Visual Basic中为 Shared) 成员都是线程安全的 但不保证所有实例成员都是线程安全的 那么线程安全到底是什么意思?

  其实线程安全很简单 就是指一个函数(方法 属性 字段或者别的)在同一时间被不同线程使用 不会造成任何线程冲突的问题 就说这个东西是线程安全的

  接下来来谈谈什么样的资源是线程安全的

  之所以使用资源这个词 是因为线程冲突不仅仅会发生在共享的变量上 两个线程同时对同一个文件进行读写 两个程序同时用同一个端口与同一个地址进行通信 都会造成线程冲突 只不过是操作系统和帮我们协调了这些冲突而已

  一个线程安全的资源即是指 在不同线程中使用不会导致线程冲突问题的资源

  一个不能被改变的资源是线程安全的 比如说一个常量

  

  const decimal pai = ;

  //C++: const double pai = ;

  因为pai的值不可能被改变 所以在不同的线程中使用也不会造成冲突 换言之它在不同的线程中同时被使用和在一个线程中被使用是没有区别的 所以这个东西线程安全的

  同样的 在 NET中 一个字符串的实例也是线程安全的 因为字符串的实例在 NET中也是不可以被改变的 一个字符串的实例一旦被创建 对其所有的属性 方法调用的结果都是唯一确定的 永远不会改变的 所以 NET类库参考中String类型才有 此类型是线程安全的 与之类似的Type类型 Assembly类型 都是线程安全的

  但string的实例是线程安全的 却不代表string的变量是线程安全的 换言之 假设有一个静态变量

  

  public static string str = ;

  str不是线程安全的 因为str这个变量的字符串实例可以被任何线程修改

  再考虑这样的例子

  public static readonly SqlConnection connection = new SqlConnection( connectionString );

  虽然connection本身虽然是线程安全的 但connection的任何成员都不是线程安全的

  比如说 我在一个线程中对这个connection调用了Open方法 然后进行查询操作 但在同一时刻 另一个线程调用了Close方法 这时候 就出现错误了

  但 单纯的使用connection而不使用其任何成员 比如说if ( connection != null )这样的代码 是不存在线程冲突的

  线程安全的资源其实还有很多 在此不一一赘述

  对于 NET Framework的类型的成员来说 只读的字段是线程安全的

  那么对于属性和方法来说 怎么知道是不是线程安全的?

  三 线程安全的函数

  因为属性和方法都是函数组成的 所以我们探讨一下什么是线程安全的函数

  上面我们说到 线程冲突的必要条件是多线程和共享资源 那么如果一个函数里面没有使用任何可能共享的资源 那么就不可能出现线程冲突 也就是线程安全的 比如说这样的函数

  

  public static int Add( int a int b )

  

  return a + b;

  

  这个函数中所使用的所有的资源都是自己的局部变量 而函数的局部变量是储存在堆栈上的 每个线程都有自己独立的堆栈 所以局部变量不可能跨线程共享 所以这样的函数显然是线程安全的

  但值得注意的是 下面的函数不是线程安全的

  

  public static void Swap( ref int a ref int b )

  //C++: void Swap( in& a int& b )

  

  int c = a;

  a = b;

  b = c;

  

  因为ref的存在 使得函数的参数是按引用传递进来的 换言之a和b看起来是函数的局部变量 但实际上却是函数外面的东西 如果这两个东西是另一个函数的局部变量 倒也没有问题 如果这两个东西是全局变量(静态成员) 就不能确保没有线程冲突了 而在上个例子中 a和b在传入函数之时 就做了一个拷贝的动作 所以传进来的a b到底是全局变量还是静态成员都没有关系了

  同样 这样的函数也不是线程安全的

  

  public static int Add( INumber a INumber b )

  //C++: int Add( INumber* a INumber* b );

  

  return a Number + b Number;

  //C++: return a >Number + b >Number;

  

  原因在于a和b虽然是函数的内部变量没错 但a Number和b Number却不是 它们不存在于堆栈上 而是在托管堆上 可能被其他线程更改

  但只使用局部变量的函数在 NET类库中是很少的 但 NET类库中还是有那么多线程安全的函数 是为什么呢?

  因为 即使一个函数使用了共享资源 如果其所使用的共享资源都是线程安全的 则这个函数也是线程安全的

  比如说这样的函数

  

  private const string connectionString = … ;

  public string GetConnectionString()

  

  return connectionString;

  

  虽然这个函数使用了一个共享资源connectionString 但因为这个资源是线程安全的 所以这个函数还是线程安全的

  同样的 我们可以得出 如果一个函数只调用线程安全的函数 只使用线程安全的共享资源

  那么这个函数也是线程安全的

  这里有一个容易被忽略的问题 运算符 并不是所有的运算符(尤其是重载后的运算符)都是线程安全的

  四 互斥锁

  有时候我们不得不面对线程不安全的问题 比如说在一开始提出来的那个例子 多线程完成 次任务 我们怎样才能解决这个问题 一个简单的办法就是给共享资源加上互斥锁 在C#中这很简单 比如一开始的那个例子

  

  public static class Environment

  public static int count = ;//全局计数器

  

  //…

  void ThreadMethod()//运行在每个线程的方法

  

  while( true )

  

  lock ( typeof( Environment ) )

  

  if ( count >= )//如果达到任务指标

  break;//中断线程执行

  DoSomething();//完成某个任务

  count++;

  通过互斥锁 使得一个线程在使用count字段的时候 其他所有的线程都无法使用 而被阻塞等待 达到了避免线程冲突的效果

  当然 这样的锁会使得这个多线程程序退化成同时只有一个线程在跑 所以我们可以把count++提前 使得lock的范围缩小 如这样

  

  void ThreadMethod()//运行在每个线程的方法

  

  while( true )

  

  lock ( typeof( Environment ) )

  

  if ( count++ >= )//如果达到任务指标

  break;//中断线程执行

  

  DoSomething();//完成某个任务

  

  最后来聊聊SyncRoot的问题

  用 NET的一定会有很多朋友困惑 为什么对一个容器加锁 需要这样写

  lock( Container SyncRoot )

  而不是直接lock( Container )

  因为锁定一个容器并不能保证不会对这个容器进行修改 考虑这样一个容器

  

  public class Collection

  

  private ArrayList _list;

  public Add( object item )

  

  _list Add( item );

  

  public object this[ int index ]

  

  get return _list[index];

  set _list[index] = value;

  

cha138/Article/program/net/201311/13071

相关参考

知识大全 详解.NET编程过程中的线程冲突[1]

详解.NET编程过程中的线程冲突[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一什么是线

知识大全 详解.NET编程过程中的线程冲突[2]

详解.NET编程过程中的线程冲突[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  比如说我在

知识大全 详解.NET编程中的线程冲突

详解.NET编程中的线程冲突  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一什么是线程冲突  

知识大全 .Net Micro Framework线程技术详解

.NetMicroFramework线程技术详解  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  

知识大全 基于.NET的多线程编程入门

基于.NET的多线程编程入门  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!多线程在构建大型系统的时

知识大全 .NET下的多线程编程应用程序域

.NET下的多线程编程应用程序域  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一应用程序域&n

知识大全 浅析Java多线程编程中的高级技术

浅析Java多线程编程中的高级技术  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nb

知识大全 Delphi与SQL Server存储过程编程详解

Delphi与SQLServer存储过程编程详解  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  

知识大全 .NET组件中的线程辅助的学习

.NET组件中的线程辅助的学习  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在Understa

知识大全 详解.NET中的动态编译

详解.NET中的动态编译  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  代码的动态编译并执行是一