当前位置:   article > 正文

C# (江湖小新)- 多线程 (同时干几件事)_c# 多线程 同时执行program.cs

c# 多线程 同时执行program.cs

基本概念

线程

  • 被定义为程序的执行路径,也叫执行单元
  • 线程是轻量级进程;使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率

进程

  • 是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程
  • 一个进程可以包括一个或多个线程,注: 至少有一个线程
  • 进程之间是相对独立的,一个进程无法访问另一个进程的数据

查看当前系统中的进程

打开任务管理器,查看当前运行的进程

查看当前系统中的线程

在任务管理器里面查询当前总共运行的线程数

并行与串行(异步与同步)

  • 并行(异步): 多个线程同时执行任务
    • 举例:小明在烧开水的同时去洗菜了
  • 串行(同步): 一个任务执行完后才能执行下一个
    • 举例:小明在烧开水,等开水烧开后再去洗菜

线程的生命周期

  • 新建:当线程实例被创建但 Start 方法未被调用时的状况
  • 就绪:当线程准备好运行并等待 CPU 调度
  • 不可运行:下面的几种情况下线程是不可运行的:
    • 已经调用 Sleep 方法
    • 已经调用 Wait 方法
    • 通过 I/O 操作阻塞
  • 死亡状态:当线程已完成执行或已中止时的状况

主线程

  • 一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程
  • 在 C# 中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。
  • 在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程
  • 可以使用 Thread 类的 CurrentThread 属性访问线程。

举例:主线程执行

  1. internal class ThreadTest
  2. {
  3. static void Main(string[] args)
  4. {
  5. Thread th = Thread.CurrentThread;
  6. th.Name = "MainThread";
  7. Console.WriteLine("线程ID是:{0},线程名称是:{1}", th.ManagedThreadId, th.Name);
  8. }
  9. }

输出结果

线程ID是:1,线程名称是:MainThread

多线程的创建与管理 

创建

  • 线程是通过扩展 Thread 类创建的,然后在构造方法中传入委托对象。扩展的 Thread 类调用 Start() 方法来开始子线程的执行

  • 子线程不需要传参使用  ThreadStart
  1. internal class ThreadTest
  2. {
  3. static void Main(string[] args)
  4. {
  5. // 创建两个子线程
  6. Thread t1 = new Thread(new ThreadStart(PrintStr));
  7. Thread t2 = new Thread(new ThreadStart(PrintStr));
  8. t1.Start();
  9. t2.Start();
  10. }
  11. private static void PrintStr()
  12. {
  13. Thread th = Thread.CurrentThread;
  14. Console.WriteLine("线程ID是:{0}", th.ManagedThreadId);
  15. }
  16. }

输出结果

  1. 线程ID是:7
  2. 线程ID是:6

通过ThreadStart 源码,可以看到它其实是一个委托

  • 如果要向子线程中传递参数则需要使用: ParameterizedThreadStart
  • 注意:ParameterizedThreadStart委托的参数类型必须是Object的
  1. internal class ThreadTest
  2. {
  3. static void Main(string[] args)
  4. {
  5. // 创建两个子线程
  6. Thread t1 = new Thread(new ParameterizedThreadStart(PrintStrParam));
  7. Thread t2 = new Thread(new ParameterizedThreadStart(PrintStrParam));
  8. t1.Start("我是有参数1");
  9. t2.Start("我是有参数2");
  10. }
  11. private static void PrintStrParam(Object obj)
  12. {
  13. Thread th = Thread.CurrentThread;
  14. Console.WriteLine("线程ID是:{0},参数是:{1}", th.ManagedThreadId,obj);
  15. }
  16. }

输出结果

  1. 线程ID是:6,参数是:我是有参数1
  2. 线程ID是:7,参数是:我是有参数2

线程的管理与销毁

  • Thread 类提供了各种管理线程的方法,下面演示sleep() 方法的使用,用于在一个特定的时间暂停线程

  • Abort() 方法用于销毁线程;通过抛出 threadabortexception 在运行时中止线程。这个异常不能被捕获,如果有 finally 块,控制会被送至 finally 块。  注:这个方法被标记过时了,虽然依旧可以使用,但推荐使用 CancellationToken  来代替

  1. internal class ThreadTest
  2. {
  3. static void Main(string[] args)
  4. {
  5. // 创建两个子线程
  6. Thread t1 = new Thread(new ThreadStart(printSleep));
  7. t1.Start();
  8. // 主线程睡眠 1 秒
  9. Thread.Sleep(1000);
  10. // 销毁线程
  11. try
  12. {
  13. t1.Abort();
  14. }
  15. catch (ThreadAbortException e)
  16. {
  17. Console.WriteLine("进catch了吗???");
  18. }
  19. finally
  20. {
  21. Console.WriteLine("进finally了吗???");
  22. }
  23. }
  24. private static void printSleep()
  25. {
  26. for (int i = 0; i < 10; i++)
  27. {
  28. // 睡眠 500 毫秒
  29. Thread.Sleep(500);
  30. Console.WriteLine("输出数字:{0}", i);
  31. }
  32. }
  33. }

输出结果

  1. 输出数字:0
  2. Unhandled exception. 输出数字:1
  3. System.PlatformNotSupportedException: Thread abort is not supported on this platform.
  4. 输出数字:2
  5. finally了吗???

线程同步与锁

  • 所谓同步:是指在某一时刻只有一个线程可以访问变量。
  • 如果不能确保对变量的访问是同步的,就会产生错误。比如:两个人同时卖一个仓库中的同种 手机,如果不控制就可能出现超卖现象(即卖出的大于库存的)
  • c#为同步访问变量提供了一个非常简单的方式,即使用c#语言的关键字 Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行

lock块语法:

  • 需要注意,传给lock的参数不能是值类型和string类型,必须是除了string外的引用类型,而且这个引用类型对象必须是所有线程都能访问到的,否则锁不住。
  • 如果你想保护一个类的实例,一般地,你可以使用this;
  • 如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了
  • 也可以单独创建一个object对象来作为指定的锁对象

语法如下:

  1. lock(expression)
  2. {
  3. // 代码逻辑
  4. }

加锁前案例

  1. internal class ThreadTest
  2. {
  3. static void Main(string[] args)
  4. {
  5. PhoneSale phone=new PhoneSale();
  6. // 创建两个子线程
  7. Thread t1 = new Thread(new ThreadStart(phone.SalePhone));
  8. Thread t2 = new Thread(new ThreadStart(phone.SalePhone));
  9. t1.Start();
  10. t2.Start();
  11. }
  12. }
  13. public class PhoneSale
  14. {
  15. // 数量
  16. private int num = 1;
  17. public void SalePhone()
  18. {
  19. if (num > 0)
  20. {
  21. Thread.Sleep(100);
  22. num--;
  23. Console.WriteLine("卖出一部手机,还剩下 {0} 个",num);
  24. }
  25. else
  26. {
  27. Console.WriteLine("卖完了....");
  28. }
  29. }
  30. }

输出结果

  1. 卖出一部手机,还剩下 0
  2. 卖出一部手机,还剩下 -1

加锁后案例

  1. internal class ThreadTest
  2. {
  3. static void Main(string[] args)
  4. {
  5. PhoneSale phone=new PhoneSale();
  6. // 创建两个子线程
  7. Thread t1 = new Thread(new ThreadStart(phone.SalePhone));
  8. Thread t2 = new Thread(new ThreadStart(phone.SalePhone));
  9. t1.Start();
  10. t2.Start();
  11. }
  12. }
  13. public class PhoneSale
  14. {
  15. // 数量
  16. private int num = 1;
  17. public void SalePhone()
  18. {
  19. lock (this)
  20. {
  21. if (num > 0)
  22. {
  23. Thread.Sleep(100);
  24. num--;
  25. Console.WriteLine("卖出一部手机,还剩下 {0} 个", num);
  26. }
  27. else
  28. {
  29. Console.WriteLine("卖完了....");
  30. }
  31. }
  32. }
  33. }

输出结果

  1. 卖出一部手机,还剩下 0
  2. 卖完了....

多线程的优缺点

优点

  • 可以同时完成多个任务,使程序的响应速度更快
  • 多线程技术解决了多部分代码同时执行的需求,能够更好的利用cpu的资源
  • 可以设置每个任务的优先级以优化程序性能

缺点

  • 线程需要占用内存,线程越多,占用内存也越多
  • 多线程需要协调和管理,所以需要占用CPU时间以便跟踪线程
  • 线程之间对共享资源的访问会相互影响,必须解决争用共享资源的问题
  • 线程太多会导致控制太复杂

为什么程序可以多线程执行呢? 程序中的多线程与CPU的多线程有什么关系?

  • 目前电脑都是多核多CPU的,一个CPU在同一时刻只能运行一个线程,但是多个CPU在同一时刻就可以运行多个线程。
  • 线程的最大并行数量上限是CPU核心的数量,但是,往往电脑运行的线程的数量远大于CPU核心的数量,所以 还是需要CPU时间片的切换
  • CPU运行速度太快,硬件处理速度跟不上,所以操作系统进行分 时间片 
    管理
    。这样,从宏观角度来说是多线程并发的,因为CPU速度太快,察觉不到,看起来是同一时刻执行了不同的操作

更多**好看的内容**和**好玩的案例**请关注**我的微信公众号: 程序猿知秋**

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/197689
推荐阅读
相关标签
  

闽ICP备14008679号