zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Python2 新手 编码问题 吐血总结
    朴素贝叶斯算法下的情感分析——C#编程实现
    element ui输入框监听enter回车事件
    MySql的动态语句foreach各种用法比较
    Mybatis中的CDATA标签的用法
    IDEA里面报黄色/灰色的重复代码的下划波浪线的去除方案
    wamp 3.0.6(apache 2.4.23) 403 forbidden 解决办法
    less文件编译成微信小程序wxss文件
    C#返回时间格式转换成 js 字符串
    Highcharts动态添加点数据
  • 原文地址:https://www.cnblogs.com/asdyzh/p/9905828.html
Copyright © 2011-2022 走看看