• C#多线程编程技术——多线程操作(没看懂)


    线程,有时被称为轻量进程(LightWeight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

    一、进程与线程

    进程(Process)和线程(Thread)是操作系统 的基本概念,但是它们比较抽象并且不容易掌握。可以将进程理解为程序在计算机上的一次执行活动,而线程则是进程的一个实体。执行线程就体现程序的真实执行情况。

    进程的概念

    1、进程

    进程是程序在计算机上的一次执行活动。运行一个程序就相当于启动一个进程。Windows系统利用进程把工作划分为多个独立的区域,每个应用程序实例对应一个进程。每个进程所占用的资源都是互相独立的。

    进程资源包括:

    (1)一个进程堆;

    (2)一个或多个线程;

    (3)一个虚拟地址空间,该空间独立于其它进程的地址空间;

    (4)一个或多个代码段,包括.dll中的代码;

    (5)一个或多个包含全局变量的数据段;

    (6)环境字符串,包括环境变量信息;

    (7)其他资源,例如打开的句柄、其它的堆等。

    2、多进程

    多进程就是在同一计算机系统 中,同一个时刻允许两个或两个以上的进程处于运行状态。多进程具有以下特点:

    (1)进程间互相独立,可靠性高。

    (2)进程之间不共享数据,没有锁问题,结构简单。

    (3)需要跨进程边界,多进程调度开销较大。

    线程的概念

    1、线程

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

    注意:一个进程可以有多个线程,一个线程必须有一个父进程,一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

    2、多线程

    多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

    (1)多线程具有以下优点。

    可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其他的线程而不是等待,这样就大大提高了程序的效率。

    (2)多线程具有以下缺点。

            ① 线程也是程序,所有线程需要占用内存,线程越多占用内存也越多;

            ② 多程序需要协调和管理,所以需要CPU时间跟踪线程;

            ③ 线程之间对共享资源的访问会相互影响,必须 解决竞用共享资源的问题

            ④ 线程太多会导致控制太复杂,最后可能造成很多Bug。

    3、线程的生命周期

    线程生命周期开始于System.Threading.Thread类的对象被创建时,结束于线程被终止或完成执行时。

    下面列出了线程生命周期中的各种状态

    (1)未启动状态:当线程实例被创建,但Start方法未被调用时的状况。

    (2)就绪状态:当线程准备好运行并等待CPU周期时的状况。

    (3)不可运行状态:已经调用Sleep方法、Wait方法或者通过I/O操作阻塞时,线程是不可运行的。

    (4)死亡状态:当线程已完成执行或已中止时的状况。

    在C#中,Thread类用于线程的工作。它允许创建并访问多线程应用程序 中的单个线程。进程中第一个被执行的线程称为主线程。

    编写程序,获取当前的主线程,并为其命名。

    可以通过Thread类的静态属性CurrentThread获取当前执行的线程,然后通过Name属性赋值”MainThread“。

    代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Threading;
    7. namespace ConsoleApp1
    8. {
    9. internal class Program
    10. {
    11. static void Main(string[] args)
    12. {
    13. Thread th=Thread.CurrentThread;
    14. th.Name = "MainThread";
    15. Console.WriteLine("This is { 0 }", th.Name);
    16. Console.ReadLine();
    17. }
    18. }
    19. }

     完整代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Threading;
    7. namespace ConsoleApp1
    8. {
    9. internal class Program
    10. {
    11. static void Main(string[] args)
    12. {
    13. Thread th=Thread.CurrentThread;
    14. th.Name = "MainThread";
    15. Console.WriteLine("This is {0}", th.Name);
    16. Console.ReadLine();
    17. }
    18. }
    19. }

    运行结果:

    Thread类

    在.NET Framework中,所有与多线程机制应用相关的类都是放在System.Threading命名空间中的。

    Thread类的属性及说明
    属性说明
    CurrentContext获取线程正在其中执行的当前上下文
    CurrentCulture获取或设置当前线程的区域性
    CurrentPrinciple获取或设置线程的当前负责人(对基于角色的安全性而言)
    CurrentThread获取当前正在运行的线程
    CurrentUICulture获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源
    ExecutionContext获取一个ExecutionContext对象,该对象包含有关当前线程的各种上下文的信息
    IsAlive获取一个值,该值指示当前线程的执行状态
    IsBackground获取或设置一个值,该值指示某个线程是否为后台线程
    IsThreadPoolThread获取一个值,该值指示线程是否属于托管线程池
    ManagedThreadId获取当前托管线程的唯一标识符
    Name获取或设置线程的名称
    Priority获取或设置一个值,该值指示线程的高度优先级
    ThreadState获取一个值,该值包含当前线程的状态

    Thread类的方法及说明
    方法说明
    Abort在调用此方法的线程上引发ThreadAboutException,以开始终止此线程的过程。调用此方法通常会终止线程
    GetApartmentState返回表示单元状态的ApartmentState值
    GetDomain返回当前线程正在其中运行的当前域
    GetDomainID返回唯一的应用程序域标识符
    Interrupt中断处于WaitSleepJoin线程状态的线程
    Join在此实例表示的线程终止前,阻止调用线程
    ResetAbort取消当前线程所请求的Abort(Object)
    SetApartmentState在线程启动前设置其单元状态
    Sleep将当前线程挂起指定的时间
    SpinWait导致线程等待由iterations参数定义的时间量
    Start使线程得以按计划执行
    Suspend挂起线程,或者如何线程已挂起,则不起作用
    VolatileRead读取字段值。无论处理器的数目或处理器缓存的状态如何,该值都是由计算机的任何处理器写入的最新值
    VolatileWrite立即向字段写入一个值,以使该值对计算机中的所有处理器都可见

    二、线程的基本操作

    通过使用Thread类,可以对线程进行创建、休眠、挂起、恢复、终止及设置优先权等操作。

    创建线程

    在C#中创建线程时,首先需要创建一个ThreadStart委托实例,再以这个ThreadStart委托作为参数,来构造Thread实例。

    注意:Thread类拥有四种重载的构造函数,常用的一个函数接收一个ThreadStart类型的参数,而ThreadStart是一个委托,其语法格式如下:

    public delegate void ThreadStart()

    编写程序,启动创建好的线程

    代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Threading;
    7. namespace ConsoleApp1
    8. {
    9. internal class Program
    10. {
    11. //创建线程的方法,输出0至10
    12. public static void ThreadMethod()
    13. {
    14. Console.WriteLine("辅助线程开始...");
    15. for(int i = 0; i < 10; i++)
    16. {
    17. Console.WriteLine("辅助线程:{0}", i);
    18. Thread.Sleep(2000); //调用Sleep方法,使辅助线程休眠2秒
    19. }
    20. Console.WriteLine("辅助线程结束.");
    21. }
    22. static void Main(string[] args)
    23. {
    24. Console.WriteLine("主线程开始");
    25. //创建委托实例
    26. ThreadStart ts = new ThreadStart(ThreadMethod); //注册ThreadMethod方法
    27. //通过委托实例来构造Thread类
    28. Thread th = new Thread(ts);
    29. th.Start(); //启动线程
    30. for(char i='A';i<'K'; i++)
    31. {
    32. Console.WriteLine("主线程:{0}", i);
    33. Thread.Sleep(1000); //调用Sleep方法,使主线程休眠1秒
    34. }
    35. th.Join(); //主线程等待辅助线程结束
    36. Console.WriteLine("主线程结束");
    37. Console.ReadLine();
    38. }
    39. }
    40. }

    运行结果:

     在代码中,首先用户可以自定义一个静态的void方法ThreadMethod;然后在Main方法中,创建ThreadStart委托的实例ts;接着通过ts构造Thread类的实例th,这样就创建好一个线程。由于Main方法是程序的入口点,优先执行Main方法,所以Main方法是主线程,ThreadMethod方法是辅助线程;执着调用Start方法启动线程,此时主线程中的for循环执行每一次后就会休息1秒,而辅助线程每执行一次则休眠2秒;最后调用了Join方法,在辅助线程中的for循环执行完之后 停止执行主线程中的for循环。

    读了代码不是很明白。

    线程休眠

    线程的休眠是通过Thread类的Sleep方法实现的,而Thread类的实例的IsAlive属性可以判断线程是否执行完毕。

    Sleep方法有以下两种重载形式。

    (1)将当前线程挂起指定的毫秒数,语法格式如下:

    public static void Sleep (int millisecondsTimeout)

    millisecondsTimeout:线程被阻止的毫秒数。如果该参数的值为零,则该线程会将其时间的剩余部分让给任何已经准备好运行的、具有同等优先级的线程,否则会无限制阻止线程。

    (2)将当前线程挂起指定的时间,语法格式如下:

    public static void Sleep (TimeSpan timeout)

    timeout:线程被阻止的时间量的TimeSpan。

    编写程序,创建线程,并在运行时休眠5秒。

    代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.IO;
    7. using System.Threading;
    8. namespace ConsoleApp1
    9. {
    10. internal class Program
    11. {
    12. public static void Method()
    13. {
    14. Console.WriteLine("启动线程");
    15. //线程暂停5000毫秒
    16. int t = 5000;
    17. Console.WriteLine("线程暂停 {0} 秒",t / 1000);
    18. Thread.Sleep(t);
    19. Console.WriteLine("线程恢复");
    20. }
    21. static void Main(string[] args)
    22. {
    23. ThreadStart ts = new ThreadStart(Method);
    24. Thread th = new Thread(ts); //创建线程
    25. th.Start(); //启动线程
    26. Console.ReadLine();
    27. }
    28. }
    29. }

    运行结果:

    线程的挂起与恢复

    Suspend方法用于挂起线程,Resume方法用于继续执行已经挂起的线程。可以使用这两个方法进行线程的同步,和Start方法有些类似的是,在调用Suspend方法后不会立即停止,而是执行到一个安全点后挂起。

    1、Suspend方法

    挂起线程,或者如果线程已挂起,则不起作用,语法格式如下:

    public void Suspend();

    2、Rusume方法

    继续已挂起的线程,语法格式如下:

    public void Resume();

    编写程序,自定义两个线程,主线程(MainThread方法)和工作线程(WorkThread方法),主线程倒序输出,工作线程正序输出。先完成主线程,再完成工作线程。

    代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.IO;
    7. using System.Threading;
    8. namespace ConsoleApp1
    9. {
    10. internal class Program
    11. {
    12. static void Main(string[] args)
    13. {
    14. ThreadStart work = new ThreadStart(WorkThread); //创建工作线程
    15. Thread th = new Thread(work);
    16. th.Start(); //启动线程
    17. th.Suspend(); //挂起线程
    18. MainThread(); //主线程
    19. th.Resume(); //恢复线程
    20. Console.ReadLine();
    21. }
    22. static void WorkThread()
    23. {
    24. for (long i = 1; i < 1000000000; i++)
    25. {
    26. if (i % 100000000 == 0 && i != 0)
    27. {
    28. Console.WriteLine("工作线程WorkThread-->i={0}", i);
    29. }
    30. }
    31. }
    32. static void MainThread()
    33. {
    34. long gap = 0;
    35. for (long i = 1000000000; i >= 0; i--)
    36. {
    37. gap = i - 1;
    38. if (i % 100000000 == 0)
    39. {
    40. Console.WriteLine("主线程MainThread-->i={0}", i);
    41. }
    42. }
    43. Console.WriteLine();
    44. }
    45. }
    46. }

    运行如下 :与书上不一致。

    1. 主线程MainThread-->i=1000000000
    2. 主线程MainThread-->i=900000000
    3. 主线程MainThread-->i=800000000
    4. 主线程MainThread-->i=700000000
    5. 主线程MainThread-->i=600000000
    6. 主线程MainThread-->i=500000000
    7. 主线程MainThread-->i=400000000
    8. 主线程MainThread-->i=300000000
    9. 主线程MainThread-->i=200000000
    10. 主线程MainThread-->i=100000000
    11. 主线程MainThread-->i=0
    12. 工作线程WorkThread-->i=100000000
    13. 工作线程WorkThread-->i=200000000
    14. 工作线程WorkThread-->i=300000000
    15. 工作线程WorkThread-->i=400000000
    16. 工作线程WorkThread-->i=500000000
    17. 工作线程WorkThread-->i=600000000
    18. 工作线程WorkThread-->i=700000000
    19. 工作线程WorkThread-->i=800000000
    20. 工作线程WorkThread-->i=900000000

     

    在代码中,用户定义了两个方法WorkThread和MainThread。MainThread方法用于倒序输出 0~10;WorkThread方法用于正序输出 1~9;然后在Main方法中,创建ThreadStart委托的实例work;接着通过work构造Thread类的实例th,这样就创建好一个线程;最后通过start方法,启动线程。

    挂起线程使用Suspend方法。线程被挂起后,操作被停止或进入休眠状态。因此,从结果中可以看出,此时主线程正常执行,但是工作线程WorkThread没有被执行。那么要想工作线程能继续执行,就需要使用Resume方法恢复线程。

    终止线程

    线程的终止是通过Thread类的Abort方法和Join方法来实现。

    1、Abort方法

    当一个线程执行时间太长时,用户有可能要终止这个线程,这就要使用Abort方法。该方法有两种重载方式:

    public void About()        //终止进程

    public void About(0bject StateInfo)        //终止线程并提供有关线程终止的异常信息

    参数stateInfo是一个对象,包含应用程序特定的信息(如状态),该信息可供正被终止的线程使用。

    注意:在线程调用Abort方法时,会引发ThreadAbortException异常。如果没有捕获异常,线程将会终止通过。

    编写程序,启动线程,while循环5次后终止线程。

    代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.IO;
    7. using System.Threading;
    8. namespace ConsoleApp1
    9. {
    10. internal class Program
    11. {
    12. static void Main(string[] args)
    13. {
    14. ThreadStart work = new ThreadStart(WorkThread); //创建工作线程
    15. Thread th = new Thread(work);
    16. th.Start();
    17. int i = 0;
    18. while(th.IsAlive)
    19. {
    20. i++;
    21. Thread.Sleep(500);
    22. if(i==5)
    23. {
    24. th.Abort();
    25. Console.WriteLine("\r\n线程被终止");
    26. }
    27. }
    28. Console.ReadLine();
    29. }
    30. static void WorkThread()
    31. {
    32. for (long i = 1; i < 1000000000; i++)
    33. {
    34. if (i % 100000000 == 0 && i != 0)
    35. {
    36. Console.WriteLine("工作线程WorkThread-->i={0}", i);
    37. }
    38. }
    39. }
    40. }
    41. }

    运行结果如下:与书上不一样。

     

     在代码中,用户首先自定义方法WorkThread,用于输出一组数据;然后在Main方法中,创建ThreadStart委托的实例work;接着通过work构造Thread类的实例th,这样就创建好一个线程;最后通过start方法,启动工作线程。

    中止线程使用Abort方法。线程被中止,就停止运行,是无法恢复的,因为Windows会永久地删除被中止线程的所有数据。跟挂起工作线程时的结果一样,中止工作线程后,工作线程自然不会被执行。

    2、Join方法

    Join方法用于等待线程中止,如果后续的处理依赖于另一个已经终止的线程,可以调用Join方法,等待线程中止。该方法有三种重载形式:

    public void Join()

    public bool Join(int millisecondsTimeout)

    public bool Join(TimeSpan timeout)

    参数说明:

    millsecondsTimeout表示等待线程终止的毫秒数。如果线程已终止,则返回值为true,如果线程经过了millisecondsTimeout指定时间后未终止,返回值为false。

    timeout表示等待线程终止的时间量TimeSpan。如果线程已终止,则返回值为true,如果线程经过timeout时间量之后未终止,则返回值为false。

    编写程序,使用Join方法等待线程终止。

    代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.IO;
    7. using System.Threading;
    8. namespace ConsoleApp1
    9. {
    10. internal class Program
    11. {
    12. static void Main(string[] args)
    13. {
    14. ThreadStart work = new ThreadStart(WorkThread); //创建工作线程
    15. Thread th = new Thread(work);
    16. th.Start(); //启动线程
    17. th.Join(); //等待工作线程中止
    18. MainThread();
    19. Console.ReadLine();
    20. }
    21. static void WorkThread()
    22. {
    23. for (long i = 1; i < 1000000000; i++)
    24. {
    25. if (i % 100000000 == 0 && i != 0)
    26. {
    27. Console.WriteLine("工作线程WorkThread-->i={0}", i);
    28. }
    29. }
    30. Console.WriteLine("工作线程执行完毕");
    31. }
    32. static void MainThread()
    33. {
    34. long gap = 0;
    35. for (long i = 1000000000; i >= 0; i--)
    36. {
    37. gap = i - 1;
    38. if (i % 100000000 == 0)
    39. {
    40. Console.WriteLine("主线程MainThread-->i={0}", i);
    41. }
    42. }
    43. Console.WriteLine("主线程执行完毕");
    44. }
    45. }
    46. }

    运行结果如下:

    1. 工作线程WorkThread-->i=100000000
    2. 工作线程WorkThread-->i=200000000
    3. 工作线程WorkThread-->i=300000000
    4. 工作线程WorkThread-->i=400000000
    5. 工作线程WorkThread-->i=500000000
    6. 工作线程WorkThread-->i=600000000
    7. 工作线程WorkThread-->i=700000000
    8. 工作线程WorkThread-->i=800000000
    9. 工作线程WorkThread-->i=900000000
    10. 工作线程执行完毕
    11. 主线程MainThread-->i=1000000000
    12. 主线程MainThread-->i=900000000
    13. 主线程MainThread-->i=800000000
    14. 主线程MainThread-->i=700000000
    15. 主线程MainThread-->i=600000000
    16. 主线程MainThread-->i=500000000
    17. 主线程MainThread-->i=400000000
    18. 主线程MainThread-->i=300000000
    19. 主线程MainThread-->i=200000000
    20. 主线程MainThread-->i=100000000
    21. 主线程MainThread-->i=0
    22. 主线程执行完毕

     

    代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.IO;
    7. using System.Threading;
    8. namespace ConsoleApp1
    9. {
    10. internal class Program
    11. {
    12. static void Main(string[] args)
    13. {
    14. ThreadStart work = new ThreadStart(WorkThread); //创建工作线程
    15. Thread th = new Thread(work);
    16. th.Start(); //启动线程
    17. th.Join(1000); //等待工作线程中止
    18. MainThread();
    19. Console.ReadLine();
    20. }
    21. static void WorkThread()
    22. {
    23. for (long i = 1; i < 1000000000; i++)
    24. {
    25. if (i % 1000000000 == 0 && i != 0)
    26. {
    27. Console.WriteLine("工作线程WorkThread-->i={0}", i);
    28. }
    29. }
    30. Console.WriteLine("工作线程执行完毕");
    31. }
    32. static void MainThread()
    33. {
    34. long gap = 0;
    35. for (long i = 1000000000; i >= 0; i--)
    36. {
    37. gap = i - 1;
    38. if (i % 100000000 == 0)
    39. {
    40. Console.WriteLine("主线程MainThread-->i={0}", i);
    41. }
    42. }
    43. Console.WriteLine("主线程执行完毕");
    44. }
    45. }
    46. }

    运行结果如下: 

    1. 主线程MainThread-->i=1000000000
    2. 工作线程WorkThread-->i=100000000
    3. 主线程MainThread-->i=900000000
    4. 工作线程WorkThread-->i=200000000
    5. 主线程MainThread-->i=800000000
    6. 工作线程WorkThread-->i=300000000
    7. 主线程MainThread-->i=700000000
    8. 工作线程WorkThread-->i=400000000
    9. 工作线程WorkThread-->i=500000000
    10. 主线程MainThread-->i=600000000
    11. 工作线程WorkThread-->i=600000000
    12. 主线程MainThread-->i=500000000
    13. 工作线程WorkThread-->i=700000000
    14. 主线程MainThread-->i=400000000
    15. 工作线程WorkThread-->i=800000000
    16. 主线程MainThread-->i=300000000
    17. 工作线程WorkThread-->i=900000000
    18. 主线程MainThread-->i=200000000
    19. 工作线程执行完毕
    20. 主线程MainThread-->i=100000000
    21. 主线程MainThread-->i=0
    22. 主线程执行完毕

     

    1. 主线程MainThread-->i=1000000000
    2. 工作线程WorkThread-->i=100000000
    3. 主线程MainThread-->i=900000000
    4. 工作线程WorkThread-->i=200000000
    5. 主线程MainThread-->i=800000000
    6. 工作线程WorkThread-->i=300000000
    7. 主线程MainThread-->i=700000000
    8. 工作线程WorkThread-->i=400000000
    9. 工作线程WorkThread-->i=500000000
    10. 主线程MainThread-->i=600000000
    11. 工作线程WorkThread-->i=600000000
    12. 主线程MainThread-->i=500000000
    13. 工作线程WorkThread-->i=700000000
    14. 主线程MainThread-->i=400000000
    15. 工作线程WorkThread-->i=800000000
    16. 主线程MainThread-->i=300000000
    17. 工作线程WorkThread-->i=900000000
    18. 工作线程执行完毕
    19. 主线程MainThread-->i=200000000
    20. 主线程MainThread-->i=100000000
    21. 主线程MainThread-->i=0
    22. 主线程执行完毕

     

     

     

    线程的优先级

  • 相关阅读:
    密码学 | 承诺:Pedersen 承诺 + ZKP
    【京东开源项目】微前端框架MicroApp 1.0正式发布
    04 后端开发总结
    PC安装苹果虚拟机?VirtualBox 安装
    LeetCode每日一题——828. 统计子串中的唯一字符
    xml引配置文件
    MySQL 经验集总结(更新ing)
    Graphviz安装教程
    PriorityQueue类的使用及底层原理
    SaaSBase:什么是Microsoft ERP?
  • 原文地址:https://blog.csdn.net/DXB2021/article/details/125766179