c# 多线程(二) 多线程的安全
来源:互联网 发布:网络骑士txt 编辑:程序博客网 时间:2024/05/16 13:01
当多个线程使用公共代码、属性时,会出现A线程刚通过一系列的算法后得出结果,刚要输出结果的时候,却被B线程修改,此时输出的结果显然不正确。于是我们就出现了锁,即线程的同步,多个线程同时执行一段公共的代码,只允许一个线程执行,其他线程等待。接下来介绍线程的2种常用锁。
一、lock
lock(object):会锁定该段代码(object必须是引用类型,不同于Monitor,后面有介绍monitor)。
lock(this):锁定当前实例的代码,多个实例互补影响。
在静态方法中Lock(object)不允许锁定非静态方法的字段属性,在静态方法中没有this,所以在多线程中在锁定代码段值得注意这一点。
控制台例子:
using System;using System.Collections.Generic;using System.Text;using System.Threading;namespace MyDemo_Lock{ //公共函数类 public class Person { static string iAge = "0";//年龄 string name = string.Empty;//名字 public Person(string Name) { name = Name; } public void WriteInfo(object age) { lock (this) //lock (iAge) { for (int a = 0; a < 5; a++)//为了更直接的看清执行的步骤设置的 { } iAge = age.ToString(); Console.WriteLine(string.Format("{0}今年{1}岁了,快成大人了!", name, iAge)); } } } class Program { static void Main(string[] args) { Person person = new Person("jim"); Person person1 = new Person("Tom"); Thread jim = new Thread(new ParameterizedThreadStart(person.WriteInfo)); Thread tom = new Thread(new ParameterizedThreadStart(person1.WriteInfo)); jim.Start("5"); tom.Start("10"); jim.Join(); tom.Join(); Console.ReadKey(); } }}
当使用2个实例时,在调试中可以看到:lock(object)会把锁定的代码块,供先到的线程使用,使用完后下一个线程才可以使用;lock(this)会互不干涉。
二、静态类Monitor
lock与Monitor方式一样
lock(obj){...}就等同于try{Monitor.Enter()...}finally{Monitor.Exit()}
控制台例子:
using System;using System.Collections.Generic;using System.Text;using System.Threading;namespace MyDemo_Lock{ //公共函数类 public class Person { static string iAge = "0";//年龄 string name = string.Empty;//名字 //bool IsDisplaySister = false;//是开始显示 public Person(string Name) { name = Name; } public void WriteInfo(object age) { try { Monitor.Enter(this); //Monitor.Enter(iAge); for (int a = 0; a < 5; a++)//为了更直接的看清执行的步骤设置的 { } //iAge = age.ToString();//Monitor在锁定区域内不能对被锁对象的值进行修改,运行时抱错“从不同步的代码块中调用了对象同步方法” Console.WriteLine(string.Format("{0}今年{1}岁了,快成大人了!", name, age)); } finally { //Monitor.Exit(this); Monitor.Exit(iAge); } } } class Program { static void Main(string[] args) { Person person = new Person("jim"); Person person1 = new Person("Tom"); Thread jim = new Thread(new ParameterizedThreadStart(person.WriteInfo)); Thread tom = new Thread(new ParameterizedThreadStart(person1.WriteInfo)); jim.Start("5"); tom.Start("10"); jim.Join(); tom.Join(); Console.ReadKey(); } }}
本示例与lock示例基本一致,Monitor.Enter(obj),obj可以为值类型。
在使用实例化2个实例时,使用Monitor.Enter(obj),Monitor在锁定区域内不能对被锁对象的值进行修改,在释放时会报“从不同步的代码块中调用了对象同步方法”,使用Monitor.Enter(this)可以避免。
线程调用Monitor.Enter()方法锁定一个对象时,这个对象就归它所有了,其它线程想要访问这个对象,只有等待它使用Monitor.Exit()方法释放锁。为了保证线程最终都能释放锁,你可以把Monitor.Exit()方法写在try-finally结构中的finally代码块里。
对于任何一个被锁定的对象,内存中都保留下面3个信息:
1、现在持有锁的线程的引用;
2、一个预备队列,队列中保存了已经准备好获取锁的线程;
3、一个等待队列,队列中保存着当前正在等待这个对象状态改变的队列的引用。
拥有对象锁的线程准备释放锁时,使用Monitor.Pulse()方法通知等待队列中的第一个线程,该线程被调到预备队列,当拥有锁的线程释放时,该预备队列的线程获得该对象锁。
主线程启动两个线程后,这两个线程哪个先获得对象锁是未知的。
示例:姐姐通过弟弟的年龄来计算自己的年龄,来显示弟弟和姐姐的年龄信息。WriteInfo保存弟弟的年龄,ReadInfo显示姐姐的年龄,为了防止姐姐先获得对象锁,无法正常显示姐姐的年龄。
using System;using System.Collections.Generic;using System.Text;using System.Threading;namespace MyDemo_Lock{ //公共函数类 public class Person { static string iAge = "0";//年龄 string name = string.Empty;//名字 bool IsDisplaySister = false;//弟弟的年龄是否保存,true:保存 public Person(string Name) { name = Name; } public void WriteInfo(object age) { Thread.Sleep(1000); try { Monitor.Enter(this); if (IsDisplaySister) { Monitor.Wait(this); } iAge = age.ToString();//Monitor在锁定区域内不能对被锁对象的值进行修改,运行时抱错“从不同步的代码块中调用了对象同步方法” Console.WriteLine(string.Format("{0}今年{1}岁了,快成大人了!", name, iAge)); IsDisplaySister = true; Monitor.Pulse(this);//通知等待的线程,锁定对象的状态发生改变,该线程被调到预备队列 } finally { Monitor.Exit(this); } } public void ReadInfo() { try { Monitor.Enter(this); if (!IsDisplaySister) { Monitor.Wait(this);//释放排它锁,挂起该线程,直到它重新获取该锁。 } iAge = (int.Parse(iAge) + 2).ToString(); Console.WriteLine(string.Format("{0}的姐姐今年{1}岁了,快成大人了!", name, iAge)); IsDisplaySister = false; Monitor.Pulse(this); } finally { Monitor.Exit(this); } } } class Program { static void Main(string[] args) { Person person = new Person("jim"); Thread jim = new Thread(new ParameterizedThreadStart(person.WriteInfo)); Thread jimSister = new Thread(new ThreadStart(person.ReadInfo)); jimSister.Start(); jim.Start("5"); jimSister.Join(); jim.Join(); Console.ReadKey(); } }}
上面的示例也可以用lock锁定代码块,可以用Monitor来限制,因为Monitor是静态类,当姐姐先获取对象锁时Monitor来挂起当前线程,弟弟的年龄保存后,释放线程,姐姐获取该锁后继续运行。
注:有不妥之处,请指教!共同进步,谢谢
- c# 多线程(二) 多线程的安全
- C#多线程(二)
- C#多线程(二)
- C#多线程(二)
- C#多线程简述(二)
- C#多线程整理(二)
- C#多线程(二) -- ThreadStart
- 多线程之二(线程安全)
- C#多线程(二)
- C#多线程详解(二)
- C#多线程二
- c# 多线程(一) 多线程的认识
- 多线程的安全
- 《多线程的安全》
- Java多线程的安全
- Java多线程的安全
- C#多线程学习笔记(二)——带参数的多线程
- C# Socket多线程编程(二)
- Android 录音
- 电信黑莓行货手机断网问题
- storm官方wiki快速通道
- JasperReports
- 海森矩阵 Hessian matrix
- c# 多线程(二) 多线程的安全
- 输入两个字符串,比如abdbcc和abc,把abc在abdbcc中的连接次序输出
- <xliff:g>标签
- c++常用函数所在头文件一览
- JS闭包深入详解
- 产生1~n的全排列
- 收缩数据库
- PHP 使用header函数设置HTTP头的示例方法 表头
- UISegmentedControl 分段控件