欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.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);
   }
}

.Net多线程编程(误用点分析)

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....");
     }
    }
   });
}

多次运行可得下面两种结果:第一个图是未发生死锁的情形,第二个图是发生死锁的情形。

.Net多线程编程(误用点分析)

.Net多线程编程(误用点分析)

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);
}

.Net多线程编程(误用点分析)

.Net多线程编程(误用点分析)

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 =>
   {
    //业务
   });
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!