[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提升程序性能(中)html
本节要点: 编程
上节介绍了多线程的基本使用方法和基本应用示例,本节深刻介绍.NET多线程中的高级应用。安全
主要有在线程资源共享中的线程安全和线程冲突的解决方案;多线程同步,使用线程锁和线程通知实现线程同步。多线程
一、 ThreadStatic特性 性能
特性:[ThreadStatic] spa
功能:指定静态字段在不一样线程中拥有不一样的值 .net
在此以前,咱们先看一个多线程的示例: pwa
咱们定义一个静态字段: 线程
static int num = 0;
而后建立两个线程进行分别累加: 设计
new Thread(() => { for (int i = 0; i < 1000000; i++) ++num; Console.WriteLine("来自{0}:{1}", Thread.CurrentThread.Name, num); }) { Name = "线程一" }.Start();
new Thread(() => { for (int i = 0; i < 2000000; i++) ++num; Console.WriteLine("来自{0}:{1}", Thread.CurrentThread.Name, num); }) { Name = "线程二" }.Start();
运行屡次结果以下:
能够看到,三次的运行结果均不相同,产生这种问题的缘由是多线程中同步共享问题致使的,便是多个线程同时共享了一个资源。如何解决上述问题,最简单的方法就是使用静态字段的ThreadStatic特性。
在定义静态字段时,加上[ThreadStatic]特性,以下:
[ThreadStatic] static int num = 0;
两个线程不变的状况下,再次运行,结果以下:
不论运行多少次,结果都是同样的,当字段被ThreadStatic特性修饰后,它的值在每一个线程中都是不一样的,即每一个线程对static字段都会从新分配内存空间,就固然于一次new操做,这样一来,因为static字段所产生的问题也就没有了。
2. 资源共享
多线程的资源共享,也就是多线程同步(即资源同步),须要注意的是线程同步指的是线程所访问的资源同步,并不是是线程自己的同步。
在实际使用多线程的过程当中,并不是都是各个线程访问不一样的资源。
下面看一个线程示例,假如咱们并不知道线程要多久完成,咱们等待一个固定的时间(假如是500毫秒):
先定义一个静态字段:
static int result;
建立线程:
Thread myThread = new Thread(() => { Thread.Sleep(1000); result = 100; }); myThread.Start(); Thread.Sleep(500); Console.WriteLine(result);
运行结果以下:
能够看到结果是0,显然不是咱们想要的,但每每在线程执行过程当中,咱们并不知道它要多久完成,能不能在线程完成后有一个通知?
这里有不少笨的方法,好比咱们可能会想到使用一个循环来检测线程状态,这些都不是理想的。
.NET为咱们提供了一个Join方法,就是线程阻塞,能够解决上述问题,咱们使用Stopwatch来记时,
改进线程代码以下:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread myThread = new Thread(() => { Thread.Sleep(1000); result = 100; }); myThread.Start(); Thread.Sleep(500); myThread.Join(); Console.WriteLine(watch.ElapsedMilliseconds); Console.WriteLine(result);
运行结果以下:
结果和咱们想要的是一致的。
3. 线程锁
除了上面示例的方法,对于线程同步,.NET还为咱们提供了一个锁机制来解决同步,再次改进上面示例以下:
先定义一个静态字段来存储锁:
static object locker = new object();
这里咱们能够先不用考虑这个对象是什么。继续看改进后的线程:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread t1 = new Thread(() => { lock (locker) { Thread.Sleep(1000); result = 100; } }); t1.Start(); Thread.Sleep(100); lock (locker) { Console.WriteLine("线程耗时:"+watch.ElapsedMilliseconds); Console.WriteLine("线程输出:"+result); }
运行结果以下:
运行结果和上面示例同样,若是线程处理过程较复杂,能够看到耗时明显减小,这是一种用比阻塞更效率的方式完成线程同步。
4. 线程通知
前面说到了可否在一个线程完成后,通知等待的线程呢,这里.NET为咱们提供了一个事件通知的方法来解决这个问题。
4.1 AutoResetEvent
先定义一个通知对象
static EventWaitHandle tellMe = new AutoResetEvent(false);
改进上面的线程以下:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread myThread = new Thread(() => { Thread.Sleep(1000); result = 100; tellMe.Set(); }); myThread.Start(); tellMe.WaitOne(); Console.WriteLine("线程耗时:" + watch.ElapsedMilliseconds); Console.WriteLine("线程输出:" + result);
运行结果以下:
4.2 ManualResetEvent
和AutoResetEvent 相对的还有一个 ManualResetEvent 手动模式,他们的区别在于,在线程结束后ManualResetEvent 仍是能够通行的,除非手动Reset关闭。下面看一个示例:
先定义一个手动通知的对象:
static EventWaitHandle mre = new ManualResetEvent(false);
建立线程:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread myThreadFirst = new Thread(() => { Thread.Sleep(1000); result = 100; mre.Set(); }) { Name = "线程一" }; Thread myThreadSecond = new Thread(() => { mre.WaitOne(); Console.WriteLine(Thread.CurrentThread.Name + "获取结果:" + result + "("+System.DateTime.Now.ToString()+")"); }) { Name="线程二"}; myThreadFirst.Start(); myThreadSecond.Start(); mre.WaitOne(); Console.WriteLine("线程耗时:" + watch.ElapsedMilliseconds + "(" + System.DateTime.Now.ToString() + ")"); Console.WriteLine("线程输出:" + result + "(" + System.DateTime.Now.ToString() + ")");
运行结果以下:
5. Semaphore
Semaphore也是一种锁定,只不过不是独占锁,能够指定多少个线程访问代码块。上面的通知模式,在线程开启的数量不少的状况下,使用Reset()关闭时,若是不使用Sleep休眠一下,颇有可能致使某些线程没有恢复的状况下,某一线程提早关闭,对于这种很难预测的状况,.NET提供了更高级的通知方式Semaphore,能够保证在超多线程时不会出现上述问题。
先定义一个通知对象的静态字段:
static Semaphore sem = new Semaphore(2, 2);
使用循环建立100个线程:
for (int i = 1; i <= 100; i++) { new Thread(() => { sem.WaitOne(); Thread.Sleep(30); Console.WriteLine(Thread.CurrentThread.Name+" "+DateTime.Now.ToString()); sem.Release(); }) { Name="线程"+i}.Start(); }
运行结果以下:
能够看到完整的输出咱们所想要看到的结果。
6. 本节要点:
A.线程中静态字段的ThreadStatic特性,使用该字段在不一样线程中拥有不一样的值
B.线程同步的几种方式,线程锁和线程通知
C.线程通知的两种方式:AutoResetEvent /ManualResetEvent
D.Semaphore:不独占锁,能够指定多少个线程访问代码块。
多线程的更多特性,下一节继续深刻介绍。
==============================================================================================
<若是对你有帮助,记得点一下推荐哦,若有有不明白或错误之处,请多交流>
<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>
<转载声明:技术须要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>
==============================================================================================