.Net多线程编程(误用点分析)
程序员文章站
2022-07-04 20:35:06
1 共享变量问题
错误写法:
所有的任务可能会共享同一个变量,所以输出结果可能会一样。
public static void error()
{
f...
1 共享变量问题
错误写法:
所有的任务可能会共享同一个变量,所以输出结果可能会一样。
public static void error() { for(int i=0;i<10;i++) { task.run(() => { console.writeline("{0}", i); }); } }
正确写法:
将变量i赋给局部变量temp,使得每一个任务使用不同的i值。
public static void right() { for (int i = 0; i < 10; i++) { int temp = i; task.run(() => { console.writeline("{0}", temp); }); } }
2 不要清理挂起任务所需资源
错误写法:
异步输出文本内容,所以在未使用完streamreader的时候,变量sr已经离开它的作用域,调用dispose方法。
public static void error() { using (streamreader sr = new streamreader(@"d:\说明.txt", encoding.default)) { task.run(() => { console.writeline("输出:{0}",sr.readline()); }); } }
正确写法:
public static void right() { using (streamreader sr = new streamreader(@"d:\说明.txt", encoding.default)) { var task = task.run(() => { console.writeline("输出:{0}", sr.readline()); }); task.wait(); } }
3 避免锁定this,typeof(type),string
正确的做法:定义一个object类型的私有只读字段,锁定之。
4 关于waithandle.waitall的waithandles的数目必须小于等于64个
public static void error() { manualresetevent[] manualevents = new manualresetevent[65]; try { for (int i = 0; i < 65; i++) { var temp = i; task.run(() => { manualevents[temp] = new manualresetevent(false); console.writeline("{0}", temp); manualevents[temp].set(); }); } waithandle.waitall(manualevents); } catch (exception ae) { console.writeline(ae.message); } }
5 无法捕获异常的情形
try { var task = task.run(() => { throw new exception("抛异常"); }); //如果将下面这行代码注掉,则无法抛出异常 task.wait(); } catch(exception ex) { console.writeline(ex.message); }
6 是否该释放task资源
建议调用dispose,但不调用也不是一个严重的错误。
注意在task任务处于某些状态时是不允许释放资源的,否则会报错。
public static void catchexception() { try { console.writeline("开始"); var task = task.run(() => { //throw new exception("抛异常"); }); //注掉下面这行代码,观察异常结果 //task.wait(); task.dispose(); console.writeline("结束"); } catch(exception ex) { console.writeline(ex.message); } }
7 死锁演示
假设tsak1和task2都在获得第二个锁(对tsak1来说它请求的第二个锁是lockedobj2 ,而对task2来说则是lockedobj1 )之前成功获得了第一个锁,就会发生死锁。
private static readonly object lockedobj1 = new object(); private static readonly object lockedobj2 = new object(); public static void lockshow() { var task1 = task.run(() => { lock (lockedobj1) { console.writeline("get lockedobj1"); lock (lockedobj2) { console.writeline("get lockedobj2...."); } } }); var task2 = task.run(() => { lock (lockedobj2) { console.writeline("get lockedobj2"); lock (lockedobj1) { console.writeline("get lockedobj1...."); } } }); }
多次运行可得下面两种结果:第一个图是未发生死锁的情形,第二个图是发生死锁的情形。
8 不要调用thread.abort方法。
task没有提供abort方法,使用新的tpl(.net 4.0以后),不会想到这个问题,一般使用cancellationtoken来控制取消任务。
9 确保共享变量是安全的
反复运行,可观察到不一样的结果,下图所示。
public static void func() { string s = "asdfgh"; parallel.invoke( () => { s = s.replace("a", "1"); s = s.replace("s", "1s"); }, () => { s = s.replace("a", "2"); s = s.replace("s", "2s"); }, () => { s = s.replace("a", "3"); }); console.writeline(s); }
10 处理器超额申请与申请不足
public static void func() { paralleloptions po = new paralleloptions(); //超额申请,处理器只有4个逻辑内核,结果设置并行度为10且是个逻辑内核均在工作,等待的任务数量大于0. po.maxdegreeofparallelism = 10; //申请不足,处理器有4个逻辑内核,却指定并行度为3,还有一个空闲的内核没有被占用(也有可能被其他线程占用,这里假设在指定并行度为3的情况下,另一个内核空闲) po.maxdegreeofparallelism = 3; list<int> list = new list<int>(); parallel.foreach(list, po, m => { //业务 }); }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!