Thread(线程)四

今天继续讲讲线程的异常处理、线程取消、多线程的临时变量和线程安全lock的问题。

1、异步处理。

一般来说如果是同步方法的异步处理,我们大多都是try catch住,但是异步方法应该怎么做呢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#region 异常处理
 
            //多线程的委托是不允许异常的, try catch包住,写下日志
            for (int i = 0; i < 20; i++)
            {
                string name = string.Format("btnThreadCore_Click{0}", i);
                Action<object> act = t =>
                {
                    try
                    {
                        Thread.Sleep(2000);
                        if (t.ToString().Equals("btnThreadCore_Click11"))
                        {
                            throw new Exception(string.Format("{0} 执行失败", t));
                        }
                        if (t.ToString().Equals("btnThreadCore_Click12"))
                        {
                            throw new Exception(string.Format("{0} 执行失败", t));
                        }
                        Console.WriteLine("{0} 执行成功", t);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                };
               taskList.Add(taskFactory.StartNew(act, name));
            }
           Task.WaitAll(taskList.ToArray());
            #endregion

  

2、线程取消。

Task不能主动取消,就好比向CPU发起了一个请求,但是你中途想中断这个请求,在正常情况下是做不到的,

同样,线程也做不到这一点,只有通过检测信号量的方式,来检测,使其线程本身来做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
CancellationTokenSource cts = new CancellationTokenSource();
 
              for (int i = 0; i < 40; i++)
              {
                  string name = string.Format("btnThreadCore_Click{0}", i);
                  Action<object> act = t =>
                  {
                      try
                      {
                          
                          Thread.Sleep(2000);
                          if (t.ToString().Equals("btnThreadCore_Click11"))
                          {
                              throw new Exception(string.Format("{0} 执行失败", t));
                          }
                          if (t.ToString().Equals("btnThreadCore_Click12"))
                          {
                              throw new Exception(string.Format("{0} 执行失败", t));
                          }
                          if (cts.IsCancellationRequested)
                          {
                              Console.WriteLine("{0} 放弃执行", t);
                          }
                          else
                          {
                              Console.WriteLine("{0} 执行成功", t);
                          }
                      }
                      catch (Exception ex)
                      {
                          cts.Cancel();
                          Console.WriteLine(ex.Message);
                      }
                  };
                  taskList.Add(taskFactory.StartNew(act, name);//没有启动的任务  在Cancel后放弃启动
              }
              Task.WaitAll(taskList.ToArray());

  

通过代码运行可以看到会出现三种结果,那么这三种结果是什么情况下出现的呢,

执行成功和执行失败这两种情况应该好理解,,放弃执行是在执行失败出现时,捕获住了异常信息,然后通过cts.Cancel();使信号量改变,

然后通过cts.IsCancellationRequested判断,这就出现了只要是出现了执行失败,后面都是放弃执行的情况。

3、多线程临时变量

1
2
3
4
5
6
7
8
9
10
for (int i = 0; i < 5; i++)
     {
       
         new Action(() =>
         {
             //Thread.Sleep(100);
             Console.WriteLine(i);
           
         }).BeginInvoke(nullnull);
     }

执行这么一段关键代码,会出现什么样的结果呢。

出现5个5,这是为什么呢,怎么和我们想的不一样,按理说不应该是出现0、1、2、3、4这样的数?

这里因为for循环是一定会比线程调用快,每一遍循环完,只是提交了线程,还没有调用,当调用时,循环已经结束,额调用时只会取最后i的值。

这就会出现5个5的情况,那么如何才能出现我们想要的结果呢。

1
2
3
4
5
6
7
8
9
10
for (int i = 0; i < 5; i++)
{
    int k = i;
    new Action(() =>
    {
        Thread.Sleep(100);
       // Console.WriteLine(i);
        Console.WriteLine(k);
    }).BeginInvoke(nullnull);
}

只需要在循环体中加一个变量存储i的值,就可以了。 

4、线程安全 lock

关于线程安全,有的人太过于重视,而也有的人一点也不关心。那么我们应该怎么做线程安全呢。

1
2
3
4
5
6
7
8
9
10
11
private int TotalCount = 0;             <br>               for (int i = 0; i < 10000; i++)
{
   
    taskList.Add(taskFactory.StartNew(() =>
        {                        
                this.TotalCount += 1;                    
        }));
}
Task.WaitAll(taskList.ToArray());
 
Console.WriteLine(this.TotalCount);

先来看看这段代码,可能大多数人会认为结果会是10000这样的结果,但是事实呢

你会发现,第一次是9997,第二次9998,第三次是9996,没有一次出现我们想要的结果。这又是为什么,

因为我们声明的private int TotalCount = 0,是共有变量,所有的线程都是调用同一个,这就出现了线程安全的问题。

那么我们应该如何解决这种情况呢,这就要加一把锁。

                           private static object btnThreadCore_Click_Lock = new object();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<em id="__mceDel">               for (int i = 0; i < 10000; i++)
                {
               
                    taskList.Add(taskFactory.StartNew(() =>
                        {
                            
                            lock (btnThreadCore_Click_Lock)
                            {
                                this.TotalCount += 1;
                               
                            }
                        }));
                }
                Task.WaitAll(taskList.ToArray());
 
                Console.WriteLine(this.TotalCount);
              </em> 

这样在执行相加的时候只会允许一个线程进行相加。

 

讲完这四点,再来说说Await/Async,这两个一般都是同时出现

 1、只出现Async,会出现一个警告,合普通线程没什么区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static async void NoReturnNoAwait()
      {
          //主线程执行
          Console.WriteLine("NoReturnNoAwait Sleep before Task,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
          Task task = Task.Run(() =>//启动新线程完成任务
          {
              Console.WriteLine("NoReturnNoAwait Sleep before,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
              Thread.Sleep(3000);
              Console.WriteLine("NoReturnNoAwait Sleep after,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
          });
 
          //主线程执行
          Console.WriteLine("NoReturnNoAwait Sleep after Task,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
      }

  

同时出现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static async void NoReturn()
{
    //主线程执行
    Console.WriteLine("NoReturn Sleep before await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
    TaskFactory taskFactory = new TaskFactory();
    Task task = taskFactory.StartNew(() =>
    {
        Console.WriteLine("NoReturn Sleep before,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(3000);
        Console.WriteLine("NoReturn Sleep after,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
    });
    await task;
    //子线程执行   其实是封装成委托,在task之后成为回调(编译器功能  状态机实现)
    Console.WriteLine("NoReturn Sleep after await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
}

  

带有await时,后面执行时,会发现也是子线程在执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static async Task NoReturnTask()
     {
         //这里还是主线程的id
         Console.WriteLine("NoReturnTask Sleep before await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
 
         Task task = Task.Run(() =>
         {
             Console.WriteLine("NoReturnTask Sleep before,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
             Thread.Sleep(3000);
             Console.WriteLine("NoReturnTask Sleep after,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
         });
         await task;
         Console.WriteLine("NoReturnTask Sleep after await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
 
         //return new TaskFactory().StartNew(() => { });  不能return
     }

  这种和前面的没什么区别,只不过它带有返回值Task。其运行结果是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static async Task<long> SumAsync()
      {
          Console.WriteLine("SumAsync {1} start ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
          long result = 0;
 
          await Task.Run(() =>
          {
              for (int k = 0; k < 10; k++)
              {
                  Console.WriteLine("SumAsync {1} await Task.Run ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, k);
                  System.Threading.Thread.Sleep(1000);
              }
 
              for (long i = 0; i < 999999999; i++)
              {
                  result += i;
              }
          });
          Console.WriteLine("SumAsync {1} end ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
          return result;
      }

  如果是带有返回值的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static async Task<long> SumAsync()
{
    Console.WriteLine("SumAsync {1} start ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
    long result = 0;
 
    await Task.Run(() =>
    {
   
        for (long i = 0; i < 999999999; i++)
        {
            result += i;
        }
    });
    Console.WriteLine("SumAsync {1} end ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
    return result;
}

  

1
2
3
4
5
Task<long> t = SumAsync();
 
long lResult = t.Result;//访问result   主线程等待Task的完成
//t.Wait();//等价于上一行
Console.WriteLine(lResult);

  

 

转-https://www.cnblogs.com/xima/p/7285960.html

posted on 2018-11-04 21:02  asdyzh  阅读(330)  评论(0编辑  收藏  举报

导航