• C# 多线程编程及其几种方式


    引言:

    进程(process):应用程序的实例要使用的资源的集合。每个进程被赋予了一个虚拟地址空间,确保在一个进程中使用的代码和数据无法由另一个进程访问。

    线程(thread):程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,及不同的线程可以执行相同的函数。

    多线程编程优缺点,

    优点:可以提高CPU利用率。

    缺点:

    1、线程越多占用内存越多;

    2、多线程需要协调和管理,需要CPU时间跟踪线程;

    3、线程之间对共享资源会相互影响,必须解决竞用共享资源问题;

    4、线程太多会导致控制太复杂,可能造成很多BUG

    一、用Thread类开启线程

      关键字:前台线程,后台线程,线程优先级,线程休眠,线程阻塞。

      前台线程:主线程结束后,前台线程将继续执行才结束。

      后台线程:主线程结束后,后台线程不管有没有完成也结束线程

     1 class MultiThreadingApplication {
     2         static void Main(string[] args) {
     3             //Thread thread1 = new Thread(new ThreadStart(Test1));
     4             Thread thread1 = new Thread(Test1);//线程传入无参数委托实例
     5             //Thread thread2 = new Thread(new ParameterizedThreadStart(Test2));//正常传递
     6             Thread thread2 = new Thread(Test2);//简化传递
     7             thread1.Name = "线程1";
     8             thread1.IsBackground = true;//设为后台线程,主线成结束后台线程自动结束;前台线程在主线程结束后继续执行才结束
     9             thread2.Name = "线程2";
    10             thread1.Start();
    11             thread2.Start("HelloWorld");
    12             Thread thread3 = new Thread(() =>
    13             {
    14                 Console.WriteLine("线程3开始");
    15                 Console.WriteLine("线程3阻塞5秒钟");
    16                 Thread.CurrentThread.Join(TimeSpan.FromSeconds(5));
    17                 Console.WriteLine("{0}的执行方法",Thread.CurrentThread.Name);
    18                 Console.WriteLine("线程3结束");
    19             });
    20             thread3.Name = "线程3";
    21             thread3.Priority = ThreadPriority.Highest;//线程优先级枚举设定,此时最高,在线程池中会优先开始
    22             thread3.Start();
    23             Console.WriteLine("Main()主函数线程结束");
    24         }
    25 
    26         static void Test1() {
    27             Console.WriteLine("线程1开始");
    28             Console.WriteLine("线程1休眠2秒钟");
    29             Thread.Sleep(2000);
    30             Console.WriteLine("{0}调用的无参数方法",Thread.CurrentThread.Name);
    31             Console.WriteLine(Thread.CurrentThread.Name+"结束");
    32         }
    33 
    34         static void Test2(object s) {
    35             Console.WriteLine("线程2开始");
    36             Console.WriteLine("{0}调用的有参数方法,方法的参数是:{1}", Thread.CurrentThread.Name, s);
    37             Console.WriteLine(Thread.CurrentThread.Name + "结束");
    38         }
    39     }  

       Thread Join()使用,在两个线程调用之间使用,阻塞当前线程,直到另一个线程结束。将两个交替的线程合并为顺序执行的线程。

    比如在主线程中调用后台子线程的方法Join(),直到后台子线程执行完毕,主线程才继续执行。

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("主线程开始");
     4             var t = new Thread(() =>
     5             {
     6                 Console.WriteLine("后台子线程开始");
     7                 Thread.Sleep(3000);
     8                 Console.WriteLine("后台子线程");
     9                 Console.WriteLine("后台子线程结束");
    10             });
    11             t.IsBackground = true;
    12             t.Start();
    13             t.Join();//等待后台子线程执行完成,才继续执行主线程,主线程在这里被阻塞。
    14             //t.Join(TimeSpan.FromSeconds(5));
    15             //t.Join(5000);//主线程阻塞等待后台子线程执行5秒钟,然后继续执行主线程,5秒后不管后台子线程是否执行完成。
    16             //Thread.CurrentThread.Join();//死锁情况,A线程和B线程为同一个线程,将一直阻塞。
    17             Console.WriteLine("主线程结束");
    18         }

     二、通过线程池类ThreadPool开启线程

     1 static void Main(string[] args)
     2         {
     3             Console.WriteLine("主线程开始");
     4             ThreadPool.QueueUserWorkItem(DownLoadFile);
     5             //使用ThreadPool线程池开启一个线程
     6             ThreadPool.QueueUserWorkItem((p) =>
     7             {
     8                 Console.WriteLine("是否线程池线程:{0}",Thread.CurrentThread.IsThreadPoolThread);
     9                 Console.WriteLine("开启了一个线程,线程ID:{0}",Thread.CurrentThread.ManagedThreadId);
    10             });
    11             Thread.Sleep(5000);
    12             Console.WriteLine("主线程结束");
    13         }
    14 
    15         static void DownLoadFile(object state)
    16         {
    17             Console.WriteLine("开始下载...    线程ID:" + Thread.CurrentThread.ManagedThreadId);
    18             Thread.Sleep(2000);
    19             Console.WriteLine("下载完成!");
    20         }

     三、Task或TaskFactory方式开启,叫法不同了,任务并行编程

    Task需要启动任务执行,TaskFactory创建及开始执行

    Task开启的是后台线程

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("主线程开始");
     4             //.net framework 4.0 TPL(Task Parallel Library, 简称TPL)任务并行库方式,类似于线程处理方式,抽象级别更高
     5             //任务并行,一个或多个任务同时运行
     6             //系统资源的使用效率更高,可伸缩性更好
     7             //TPL提供了一组简单丰富的 API,这些 API 支持等待、取消、继续、可靠的异常处理、详细状态、自定义计划等功能。
     8             //降低多线程编码和并行编程的复杂度,提升开发效率
     9             //使用Task开启一个任务(其实也是一个线程)
    10             Task task = new Task(new Action(() =>
    11             {
    12                 Console.WriteLine("是否线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread);
    13                 Console.WriteLine("开启了一个线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
    14             }));
    15             task.Start();
    16 
    17             Task.Factory.StartNew(() =>
    18             {
    19                 Console.WriteLine("是否线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread);
    20                 Console.WriteLine("开启了一个线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
    21             });
    22 
    23             TaskFactory tf = new TaskFactory();
    24             tf.StartNew(() =>
    25             {
    26                 Console.WriteLine("是否线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread);
    27                 Console.WriteLine("开启了一个线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
    28             });29 
    30             Thread.Sleep(2000);
    31             Console.WriteLine("主线程结束");
    32         }

     CancellationToken 传入取消令牌,外部控制任务内部结束

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("主线程开始");
     4             var cts = new CancellationTokenSource();
     5             var task = new Task<int>(()=> {
     6                 return TaskAction("task", 10, cts.Token);
     7             });
     8             task.Start();
     9             Console.WriteLine(task.Status);
    10             cts.Cancel();
    11             Console.WriteLine(task.Status);
    12             task.Wait();
    13             Thread.Sleep(2000);
    14             Console.WriteLine(task.Status);
    15             Console.WriteLine("task结果:{0}",task.Result);
    16             Console.WriteLine("主线程结束");
    17         }
    18 
    19         static int TaskAction(string name, int seconds, CancellationToken token) {
    20             Console.WriteLine("Task:{0} is runing on Thread:{1}",name,Thread.CurrentThread.ManagedThreadId);
    21             for (int i = 0; i < seconds; i++)
    22             {
    23                 Thread.Sleep(1000);
    24                 if (token.IsCancellationRequested)
    25                 {
    26                     Console.WriteLine("请求取消令牌产生作用");
    27                     return -1;
    28                 }
    29             }
    30             return 42 * seconds;
    31         }

     创建任务集合及返回结果

     1 static void Main(string[] args)
     2         {
     3             //Task<T>,Result等待任务调用完成得到结果,有Wait的作用
     4             var tasks = new List<Task<string>>() {
     5                 Task.Factory.StartNew(()=> {
     6                     return "task1";
     7                 }),
     8                 Task.Factory.StartNew(()=> {
     9                     return "task2";
    10                 }),
    11                 Task.Factory.StartNew(()=> {
    12                     return "task3";
    13                 }),
    14             };
    15 
    16             foreach (var task in tasks)
    17             {
    18                 Console.WriteLine(task.Result);
    19             }
    20         }

    异常捕获

    通过Catch捕获Task的异常是AggregateException,一个被封装的异常,需要通过InnerException访问底层异常

    推荐使用GetWaiter和GetResult方法访问Task的结果,可以获取到原始异常;

    通过ContinueWith处理OnlyOnFaulted事件,捕获的异常也是一个被封装的异常,需要通过InnerException访问底层异常

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("主线程开始");
     4             Task<int> task = null;
     5             try
     6             {
     7                 task = Task.Run(() => TaskExceptionAction("Task1", 2));
     8                 var result = task.Result;
     9                 Console.WriteLine(result);
    10             }
    11             catch (Exception e)
    12             {
    13                 Console.WriteLine("Task 1 Exception,type:" + e.GetType() + ",Message:" + e.Message);
    14             }
    15             Console.WriteLine("==============================");
    16             try
    17             {
    18                 task = Task.Run(() => TaskExceptionAction("Task2", 2));
    19                 var result = task.GetAwaiter().GetResult();
    20                 Console.WriteLine(result);
    21             }
    22             catch (Exception e)
    23             {
    24                 Console.WriteLine("Task 2 Exception,type:" + e.GetType() + ",Message:" + e.Message);
    25             }
    26 
    27             var task3 = new Task<int>(() =>
    28             {
    29                 return TaskExceptionAction("task3", 2);
    30             });
    31             var continueTask = Task.WhenAll(task3);
    32             continueTask.ContinueWith(t =>
    33             {
    34                 Console.WriteLine("Task 3 Exception,type:" + t.Exception.GetType() + ",Message:" + t.Exception.Message);
    35             }, TaskContinuationOptions.OnlyOnFaulted);
    36             task3.Start();
    37             task3.Wait();
    38             Console.WriteLine("主线程结束");
    39         }
    40 
    41         static int TaskExceptionAction(string name, int seconds)
    42         {
    43             Console.WriteLine("任务:{0} 在线程:{1}上运行", name, Thread.CurrentThread.ManagedThreadId);
    44             Thread.Sleep(seconds * 1000);
    45             throw new Exception("Error");
    46         }

     多任务的串行化

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("主线程开始");
     4             var queryTask = new Task<string>(() =>
     5             {
     6                 Console.WriteLine("Start queryTask!");
     7                 return "QueryResult";
     8             });
     9             var analyzeTask = queryTask.ContinueWith((queryResult) =>
    10             {
    11                 Console.WriteLine("Start AnalyzeTask!");
    12                 return "Analyzed Data:" + queryResult.Result;
    13             });
    14             var reportTask = analyzeTask.ContinueWith((analyzeResult) =>
    15             {
    16                 Console.WriteLine("Start ReportTask!");
    17                 return "Reporting Data:" + analyzeResult.Result;
    18             });
    19             queryTask.Start();
    20             Console.WriteLine(reportTask.Result);
    21             Console.WriteLine("主线程结束");
    22         }

     除非所有子任务(子任务的子任务)结束运行,否则创建任务(父任务)不会认为已经结束

    父任务异常不会影响子任务执行

    子任务异常不会影响父任务执行

     1  static void Main(string[] args)
     2         {
     3             Console.WriteLine("主线程开始");
     4             var parentTask = Task.Factory.StartNew(() =>
     5             {
     6                 Console.WriteLine("创建一个父任务");
     7                 var subTask = Task.Factory.StartNew(() =>
     8                 {
     9                     Console.WriteLine("创建一个子任务");
    10                     throw new Exception("subTask Error");
    11                 }, TaskCreationOptions.AttachedToParent);
    12                 Console.WriteLine("父任务结束");
    13             });
    14             Thread.Sleep(5000);
    15             Console.WriteLine("主线程结束");
    16         }

     四、异步委托开启多线程

    //TODO

    部分内容来自:https://www.cnblogs.com/tianqing/p/6970331.html

  • 相关阅读:
    Shell IFS
    Crontab
    linux awk
    free
    条件语句练习2
    条件语句练习
    打印菜单
    条件测试语法
    read 命令
    jQuery(实例)
  • 原文地址:https://www.cnblogs.com/mojiejushi/p/13194341.html
Copyright © 2020-2023  润新知