三沣开发知识 购物 网址 游戏 小说 歌词 地图 快照 开发 股票 美女 新闻 笑话 | 汉字 软件 日历 阅读 下载 图书馆 编程 China
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题
autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程 CSS/HTML/Xhtml
html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
站长资讯 .NET新手 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA VisualStudio ASP.NET-MVC .NET控件开发 EntityFramework WinRT-Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动 Html-Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP OracleERP DynamicsCRM K2 BPM 信息安全 企业信息 Android开发 iOS开发 WindowsPhone WindowsMobile 其他手机 敏捷开发 项目管理 软件工程 SQLServer Oracle MySQL NoSQL 其它数据库 Windows7 WindowsServer Linux
  IT知识库 -> .NET新手区 -> 多线程(6)线程同步 -> 正文阅读
 

[.NET新手区]多线程(6)线程同步

多线程(6)线程同步    使用多线程很容易,但是如果多个线程同时访问一个共享资源时而不加以控制,就会导致数据损坏。所以多线程并发时,必须要考虑线程同步(或称线程安全)的问题。 
什么是线程同步
多个线程同时访问共享资源时,使多个线程顺序(串行)访问共享资源的机制。
注意:
1,共享资源,比如全局变量和静态变量。
2,访问,一般指写操作,读操作无需考虑线程同步。
3,串行,指当一个线程正在访问共享资源时,其它线程等待,直到该线程释放锁。
线程同步带来哪些问题
如果能保证多个线程不会同时访问共享资源,那么就不需要考虑线程同步。
虽然线程同步能保证多线程同时访问共享数据时线程安全,但是同时也会带来以下问题:
1,使用起来繁琐,因为必须找出代码中所有可能由多个线程同时访问的共享数据,并且要用额外的代码将这些代码包围起来,获取和释放一个线程同步锁,而一旦有一处忘记用锁包围,共享数据就会被损坏。
2,损害性能,因为获取和释放一个锁是需要时间的。
3,可能会造成更多的线程被创建,由于线程同步锁一次只允许一个线程访问共享资源,当线程池线程试图获取一个暂时无法获取的锁时,线程池就会创建一个新的线程。
所以,要从设计上尽可能地避免线程同步,实在不能避免的再考虑线程同步。
线程同步的常用解决方案
1,锁
包括lock关键字和Monitor类型。
使用lock关键字实现:
 



 1 /// <summary>
 2 /// 线程同步计算器
 3 /// </summary>
 4 public class SyncCounter : CounterBase
 5 {
 6     /// <summary>
 7     /// 全局变量
 8     /// </summary>
 9     public int Result = 0;
10 
11     private static readonly object lockObj = new object();
12 
13     public override void Increase()
14     {
15         lock (lockObj)
16         {
17             Result++;
18         }
19     }
20 
21     public override void Decrease()
22     {
23         lock (lockObj)
24         {
25             Result--;
26         }
27     }
28 }

View Code
需要注意的是:
1,lock锁定的对象必须是引用类型,不能是值类型。因为值类型传入会发生装箱,这样每次lock的将是一个不同的对象,就没有办法实现多线程同步了。
2,避免使用public类型的对象,这样很容易导致死锁。因为其它代码也有可能锁定该对象。
3,避免锁定字符串,因为字符串会被CLR暂留(也就是说两个变量的字符串内容相同,.net会把暂留的字符串对象分配给变量),导致应用程序中锁定的是同一个对象,造成死锁。
使用Monitor实现:
 



 1 /// <summary>
 2 /// 线程同步计算器
 3 /// </summary>
 4 public class SyncCounter : CounterBase
 5 {
 6     /// <summary>
 7     /// 全局变量
 8     /// </summary>
 9     public int Result = 0;
10 
11     private static readonly object lockObj = new object();
12 
13     public override void Increase()
14     {
15         Monitor.Enter(lockObj);
16         try
17         {
18             Result++;
19         }
20         finally
21         {
22             Monitor.Exit(lockObj);
23         }
24     }
25 
26     public override void Decrease()
27     {
28         Monitor.Enter(lockObj);
29         try
30         {
31             Result--;
32         }
33         finally
34         {
35             Monitor.Exit(lockObj);
36         }
37     }
38 }

View Code
完整代码:


  1 namespace ConsoleApplication28
  2 {
  3     class Program
  4     {
  5         static void Main(string[] args)
  6         {
  7             //同时发起3个异步线程
  8             Console.WriteLine("普通(非线程同步)计算器测试...");
  9             var normalCounter = new NormalCounter();
 10             var tasks = new List<Task>();
 11             var task1 = Task.Factory.StartNew(() =>
 12             {
 13                 TestCounter(normalCounter);
 14             });
 15             tasks.Add(task1);
 16 
 17             var task2 = Task.Factory.StartNew(() =>
 18             {
 19                 TestCounter(normalCounter);
 20             });
 21             tasks.Add(task2);
 22 
 23             var task3 = Task.Factory.StartNew(() =>
 24             {
 25                 TestCounter(normalCounter);
 26             });
 27             tasks.Add(task3);
 28 
 29 
 30             Task.WaitAll(tasks.ToArray());
 31             Console.WriteLine("NormalCounter.Result:" + normalCounter.Result);
 32             Console.WriteLine("*******************************************");
 33 
 34             Console.WriteLine("线程同步计算器测试...");
 35             var syncCounter = new SyncCounter();
 36             var tasks1 = new List<Task>();
 37             task1 = Task.Factory.StartNew(() =>
 38             {
 39                 TestCounter(syncCounter);
 40             });
 41             tasks1.Add(task1);
 42 
 43             task2 = Task.Factory.StartNew(() =>
 44             {
 45                 TestCounter(syncCounter);
 46             });
 47             tasks1.Add(task2);
 48 
 49             task3 = Task.Factory.StartNew(() =>
 50             {
 51                 TestCounter(syncCounter);
 52             });
 53             tasks1.Add(task3);
 54 
 55             Task.WaitAll(tasks1.ToArray());
 56             Console.WriteLine("SyncCounter.Result:" + syncCounter.Result);
 57 
 58             Console.ReadKey();
 59         }
 60 
 61         /// <summary>
 62         /// 
 63         /// </summary>
 64         /// <param name="counter"></param>
 65         static void TestCounter(CounterBase counter)
 66         {
 67             //100000次加减
 68             for (int i = 0; i < 100000; i++)
 69             {
 70                 counter.Increase();
 71                 counter.Decrease();
 72             }
 73         }
 74     }
 75 
 76     /// <summary>
 77     /// 计算器基类
 78     /// </summary>
 79     public abstract class CounterBase
 80     {
 81         /// <summary>
 82         /// 83         /// </summary>
 84         public abstract void Increase();
 85 
 86         /// <summary>
 87         /// 88         /// </summary>
 89         public abstract void Decrease();
 90     }
 91 
 92     /// <summary>
 93     /// 普通计算器
 94     /// </summary>
 95     public class NormalCounter : CounterBase
 96     {
 97         /// <summary>
 98         /// 全局变量
 99         /// </summary>
100         public int Result = 0;
101 
102         public override void Increase()
103         {
104             Result++;
105         }
106 
107         public override void Decrease()
108         {
109             Result--;
110         }
111 
112     }
113 
114     /// <summary>
115     /// 线程同步计算器
116     /// </summary>
117     public class SyncCounter : CounterBase
118     {
119         /// <summary>
120         /// 全局变量
121         /// </summary>
122         public int Result = 0;
123 
124         private static readonly object lockObj = new object();
125 
126         public override void Increase()
127         {
128             lock (lockObj)
129             {
130                 Result++;
131             }
132         }
133 
134         public override void Decrease()
135         {
136             lock (lockObj)
137             {
138                 Result--;
139             }
140         }
141     }
142 }

View Code
  
lock关键字揭密:
 通过查看lock关键字生成的IL代码,如下图:

从上图可以得出以下结论:
lock关键字内部就是使用Monitor类(或者说lock关键字是Monitor的语法糖),使用lock关键字比直接使用Monitor更好,原因有二。
1,lock语法更简洁。
2,lock确保了即使代码抛出异常,也可以释放锁,因为在finally中调用了Monitor.Exit方法。 
2,信号同步
信号同步机制中涉及的类型都继承自抽象类WaitHandle,这些类型有EventWaitHandle(类型化为AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex。关系如下图。

下面是使用信号同步机制的一个简单的例子,如下代码:



 1 namespace WindowsFormsApplication1
 2 {
 3     public partial class Form1 : Form
 4     {
 5         //信号
 6         AutoResetEvent autoResetEvent = new AutoResetEvent(false);
 7 
 8         public Form1()
 9         {
10             InitializeComponent();
11 
12             CheckForIllegalCrossThreadCalls = false;
13         }
14 
15         /// <summary>
16         /// 开始
17         /// </summary>
18         /// <param name="sender"></param>
19         /// <param name="e"></param>
20         private void button1_Click(object sender, EventArgs e)
21         {
22             Task.Factory.StartNew(() => 
23             {
24                 this.richTextBox1.Text+="线程启动..." + Environment.NewLine;
25                 this.richTextBox1.Text += "开始处理一些实际的工作" + Environment.NewLine;
26                 Thread.Sleep(3000);
27 
28                 this.richTextBox1.Text += "我开始等待别的线程给我信号,才愿意继续下去" + Environment.NewLine;
29                 autoResetEvent.WaitOne();
30                 this.richTextBox1.Text += "我继续做一些工作,然后结束了!";
31             });
32         }
33 
34         /// <summary>
35         /// 信号同步
36         /// </summary>
37         /// <param name="sender"></param>
38         /// <param name="e"></param>
39         private void button2_Click(object sender, EventArgs e)
40         {
41             //给在autoResetEvent上等待的线程一个信号
42             autoResetEvent.Set();
43         }
44     }
45 }

View Code
运行效果:
1,线程阻塞,等待信号。

2,主线程发送信号,让线程继续执行。

3,线程安全的集合类
我们也可以通过使用.net提供的线程安全的集合类来保证线程安全。在命名空间:System.Collections.Concurrent下。
主要包括:
ConcurrentQueue 线程安全版本的Queue【常用】 ConcurrentStack线程安全版本的Stack ConcurrentBag线程安全的对象集合 ConcurrentDictionary线程安全的Dictionary【常用】
  .NET新手区 最新文章
将ZIP文件添加到程序集资源文件然后在运行时
Web服务的调用
.NET创建WebService服务简单的例子
多线程编程学习笔记——任务并行库(三)
序列化和反序列化
Spring学习之路
.Net开发之旅(一个年少轻狂的程序员的感慨
使用FSharp 探索Dotnet图像处理功能1
Linq学习(五)
.net新手学习框架之一
上一篇文章           查看所有文章
加:2017-07-06 21:52:35  更:2017-07-06 21:54:50 
 
技术频道: 站长资讯 .NET新手区 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA Visual Studio ASP.NET MVC .NET控件开发 Entity Framework WinRT/Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动设计 Html/Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP Oracle ERP Dynamics CRM K2 BPM 信息安全 企业信息化其他 Android开发 iOS开发 Windows Phone Windows Mobile 其他手机开发 敏捷开发 项目与团队管理 软件工程其他 SQL Server Oracle MySQL NoSQL 其它数据库 Windows 7 Windows Server Linux
脚本语言: vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题 autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程
网站开发: CSS/HTML/Xhtml html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年7日历
2018-7-22 22:44:29
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT知识库