[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中)
本节要点:
上节介绍了多线程的基本使用方法和基本应用示例,本节深入介绍.NET多线程中的高级应用。
主要有在线程资源共享中的线程安全和线程冲突的解决方案;多线程同步,使用线程锁和线程通知实现线程同步。
1、 ThreadStatic特性
特性:[ThreadStatic]
功能:指定静态字段在不同线程中拥有不同的值
在此之前,我们先看一个多线程的示例:
我们定义一个静态字段:
static int num = ;
然后创建两个线程进行分别累加:
new Thread(() =>
{
for (int i = ; i < ; i++)
++num;
Console.WriteLine("来自{0}:{1}", Thread.CurrentThread.Name, num);
})
{ Name = "线程一" }.Start();
new Thread(() =>
{
for (int i = ; i < ; i++)
++num;
Console.WriteLine("来自{0}:{1}", Thread.CurrentThread.Name, num);
})
{ Name = "线程二" }.Start();
运行多次结果如下:
可以看到,三次的运行结果均不相同,产生这种问题的原因是多线程中同步共享问题导致的,即是多个线程同时共享了一个资源。如何解决上述问题,最简单的方法就是使用静态字段的ThreadStatic特性。
在定义静态字段时,加上[ThreadStatic]特性,如下:
[ThreadStatic]
static int num = ;
两个线程不变的情况下,再次运行,结果如下:
不论运行多少次,结果都是一样的,当字段被ThreadStatic特性修饰后,它的值在每个线程中都是不同的,即每个线程对static字段都会重新分配内存空间,就当然于一次new操作,这样一来,由于static字段所产生的问题也就没有了。
2. 资源共享
多线程的资源共享,也就是多线程同步(即资源同步),需要注意的是线程同步指的是线程所访问的资源同步,并非是线程本身的同步。
在实际使用多线程的过程中,并非都是各个线程访问不同的资源。
下面看一个线程示例,假如我们并不知道线程要多久完成,我们等待一个固定的时间(假如是500毫秒):
先定义一个静态字段:
static int result;
创建线程:
Thread myThread = new Thread(() =>
{
Thread.Sleep();
result = ;
});
myThread.Start();
Thread.Sleep();
Console.WriteLine(result);
运行结果如下:
可以看到结果是0,显然不是我们想要的,但往往在线程执行过程中,我们并不知道它要多久完成,能不能在线程完成后有一个通知?
这里有很多笨的方法,比如我们可能会想到使用一个循环来检测线程状态,这些都不是理想的。
.NET为我们提供了一个Join方法,就是线程阻塞,可以解决上述问题,我们使用Stopwatch来记时,
改进线程代码如下:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
Thread myThread = new Thread(() =>
{
Thread.Sleep();
result = ;
});
myThread.Start();
Thread.Sleep();
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();
result = ;
}
});
t1.Start();
Thread.Sleep();
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();
result = ;
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();
result = ;
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(, );
使用循环创建100个线程:
for (int i = ; i <= ; i++)
{
new Thread(() =>
{
sem.WaitOne();
Thread.Sleep();
Console.WriteLine(Thread.CurrentThread.Name+" "+DateTime.Now.ToString());
sem.Release();
}) { Name="线程"+i}.Start();
}
运行结果如下:
可以看到完整的输出我们所想要看到的结果。
6. 本节要点:
A.线程中静态字段的ThreadStatic特性,使用该字段在不同线程中拥有不同的值
B.线程同步的几种方式,线程锁和线程通知
C.线程通知的两种方式:AutoResetEvent /ManualResetEvent
D.Semaphore:不独占锁,可以指定多少个线程访问代码块。
多线程的更多特性,下一节继续深入介绍。
==============================================================================================
<如果对你有帮助,记得点一下推荐哦,如有有不明白或错误之处,请多交流>
<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>
<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>
.NET 技术交流群:467189533
==============================================================================================
[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中)的更多相关文章
-
[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种. ...
-
[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)
[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...
-
[.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上)
[.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上) 本节导读: 随着硬件和网络的高速发展,为多线程(Multithreading) ...
-
[.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门
[.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习 ...
-
[.net 面向对象程序设计进阶] (1) 开篇
[.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...
-
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...
-
[.net 面向对象程序设计进阶] (23) 团队开发利器(二)优秀的版本控制工具SVN(上)
[.net 面向对象程序设计进阶] (23) 团队开发利器(二)优秀的版本控制工具SVN(上) 本篇导读: 上篇介绍了常用的代码管理工具VSS,看了一下评论,很多同学深恶痛绝,有的甚至因为公司使用VS ...
-
[.net 面向对象程序设计进阶] (3) 正则表达式 (二) 高级应用
[.net 面向对象程序设计进阶] (2) 正则表达式 (二) 高级应用 上一节我们说到了C#使用正则表达式的几种方法(Replace,Match,Matches,IsMatch,Split等),还 ...
-
[.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手
[.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...
随机推荐
-
EMC学习之电磁辐射
我们在接触新鲜事物的时候,通常习惯用自己熟悉的知识去解释自己不熟悉的事物.EMC知识更多的涉及到微波和射频,对于像我这种专注于信号完整性而 对EMC知识知之甚少的菜鸟来说,最初也只能用SI的一些基础知 ...
-
js/jquery 实时监听输入框值变化的完美方案:oninput &; onpropertychange
(1) 先说jquery, 使用 jQuery 库的话,只需要同时绑定 oninput 和 onpropertychange 两个事件就可以了,示例代码: $('#username').bin ...
-
NOR型flash与NAND型flash的区别
1) 闪存芯片读写的基本单位不同 应用程序对NOR芯片操作以“字”为基本单位.为了方便对大容量NOR闪存的管理,通常将NOR闪存分成大小为128KB或者64KB的逻辑块,有时候块内还分成扇区.读写时 ...
-
jqgrid 中设置列不排序
背景 今天在做系统的功能时,当时有这么个需求:在添加了一行数据时,原本的排序的自动就不能再排序,也就是排序失效. 1. 使用onSortCol事件禁止排序列 当时使用了初始化时,使用onSortCol ...
-
easyUI的datagrid控件日期列不能正确显示Json格式数据的解决方案
EasyUI是一套比较轻巧易用的Jquery控件,在使用过程中遇到一个问题,它的列表控件——datagrid, 在显示日期列的时候,由于后台返回给页面的数据是Json格式的,其中的日期字段,在后台是正 ...
-
java--内部类访问final成员
局部类只能访问外包方法中的final成员.位于方法内部的局部类,可以访问局部类之外,外包方法之内的所以变量和方法,但是生命周期不同,延长生命周期的办法就是将变量设置为final类型. 1)从程序设计语 ...
-
Google 怎么搜索
著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 作者:崔凯 链接:http://www.zhihu.com/question/20161362/answer/14180620 ...
-
Robust Locally Weighted Regression 鲁棒局部加权回归 -R实现
鲁棒局部加权回归 [转载时请注明来源]:http://www.cnblogs.com/runner-ljt/ Ljt 作为一个初学者,水平有限,欢迎交流指正. 算法参考文献: (1) Robust L ...
-
HDU 4614 Vases and Flowers 【线段树】+【二分】
<题目链接> 题目大意: 有n个花瓶,每个花瓶中只能放一朵花.两种操作,一种是从A开始放F朵花,如果有的花瓶中已经有花则跳过这个花瓶,往下一个花瓶放:第二种是将区间[A,B]之间花瓶中的花 ...
-
394. Decode String 解码icc字符串3[i2[c]]
[抄题]: Given an encoded string, return it's decoded string. The encoding rule is: k[encoded_string], ...