C# 多线程一: Thread 的简单理解与运用

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

一进程和线程的关系

二线程的组成

三多线程的实现

四C#中的线程Thread

1.命名空间

2.构造函数

3.属性

(1).常用属性

        Name

        Priority

        IsAlive

        IsBackground

        ThreadState

(2).其他属性

4.方法

(1).常用方法

        public void Start()

        public static void Sleep( int millisecondsTimeout )

        public void Abort()

        public static void ResetAbort()

        public void Join()

        public void Interrupt()

(2).其他方法

最后


一进程和线程的关系

了解线程之前首先了解下什么是进程
进程

狭义定义进程是正在运行的程序的实例。
广义定义进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元在传统的操作系统中进程既是基本的分配单元也是基本的执行单元。

进程的概念主要有两点

第一进程是一个实体。每一个进程都有它自己的地址空间一般情况下包括文本区域text region、数据区域data region和堆栈stack region。文本区域存储处理器执行的代码数据区域存储变量和进程执行期间使用的动态分配的内存堆栈区域存储着活动过程调用的指令和本地变量。

第二进程是一个“执行中的程序”。程序是一个没有生命的实体只有处理器赋予程序生命时操作系统执行之它才能成为一个活动的实体我们称其为进程。

线程

是操作系统能够进行运算调度的最小单位。它被包含在进程之中是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流一个进程中可以并发多个线程每条线程并行执行不同的任务。

二线程的组成

线程主要是由cpu寄存器  调用栈  线程本地存储器TLS 组成

cpu寄存器主要记录当前执行线程的状态

调用栈主要用来维护线程所调用的内存和数据

TLS用来存放线程的状态信息

三多线程的实现

在单核处理器的电脑中

多线程的实现是通过cup在完成一个时间切片之后在活动的线程间进行切换执行来完成的由于cup运行速度快所以看上去是同一时刻执行了多个操作实际同一时刻只有一个线程在处理。

  在多核的电脑中

多线程被实现成混合时间片和真实的并发——不同的线程在不同的CPU上运行。由于操作系统的需要服务自己的线程以及一些其他的应用程序。这几乎可以肯定仍然会出现一些时间切片。

四C#中的线程Thread

1.命名空间

System.Threading

2.构造函数

Thread(ParameterizedThreadStart)
初始化 Thread 类的新实例指定允许对象在线程启动时传递给线程的委托。要执行的方法是有参的。
实例

static void Main(string[] args) {
        object obj = "xxx";
        //第一种写法
        Thread thread = new Thread(LudwigThread);
        thread.Start(obj);
        //第二种写法 delegate
        Thread thread1 = new Thread(new ParameterizedThreadStart(LudwigThread));
        thread1.Start(obj);
        //第三种写法 lambda
        Thread thread2 = new Thread((arg) => { LudwigThread(arg); });
        thread2.Start(obj);

        Console.WriteLine("mainThread");
        Console.Read();
    }
    static void LudwigThread(object obj) {
        Console.WriteLine("LudwigThread" + obj);
    }

注可传参的写法 注意参数类型必须为object.

打印


Thread(ParameterizedThreadStart, Int32)
初始化 Thread 类的新实例指定允许对象在线程启动时传递给线程的委托并指定线程的最大堆栈大小
实例

static void Main(string[] args) {
        object obj = "xxx";
        //第一种写法
        Thread thread = new Thread(LudwigThread, 1024);
        thread.Start(obj);
        //第二种写法 delegate
        Thread thread1 = new Thread(new ParameterizedThreadStart(LudwigThread), 1024);
        thread1.Start(obj);
        //第三种写法 lambda
        Thread thread2 = new Thread((arg) => { LudwigThread(arg); }, 1024);
        thread2.Start(obj);

        Console.WriteLine("mainThread");
        Console.Read();
    }
    static void LudwigThread(object obj) {
        Console.WriteLine("LudwigThread" + obj);
    }

 注 根据.net api的解释 尽量不要使用这个重载

.Net API 地址Thread 构造函数 (System.Threading) | Microsoft Learn

Thread(ThreadStart)
初始化 Thread 类的新实例。要执行的方法是无参的。
实例

static void Main(string[] args) {
        //第一种写法
        Thread thread = new Thread(LudwigThread);
        thread.Start();
        //第二种写法 delegate
        Thread thread1 = new Thread(new ThreadStart(LudwigThread));
        thread1.Start();
        //第三种写法 lambda
        Thread thread2 = new Thread(() => { LudwigThread(); });
        thread2.Start();

        Console.WriteLine("mainThread");
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
    }

打印

 
Thread(ThreadStart, Int32)
初始化 Thread 类的新实例指定线程的最大堆栈大小。
实例

static void Main(string[] args) {
        //第一种写法
        Thread thread = new Thread(LudwigThread, 1024);
        thread.Start();
        //第二种写法 delegate
        Thread thread1 = new Thread(new ThreadStart(LudwigThread), 1024);
        thread1.Start();
        //第三种写法 lambda
        Thread thread2 = new Thread(() => { LudwigThread(); }, 1024);
        thread2.Start();

        Console.WriteLine("mainThread");
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
    }

注 根据.net api的解释 尽量不要使用这个重载

.Net API 地址Thread 构造函数 (System.Threading) | Microsoft Learn

3.属性

(1).常用属性

 Name

获取或设置线程的名称。

代码

 static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Console.WriteLine(thread.Name);
        Console.Read();
    }
    static void LudwigThread() {

    }

打印


Priority

获取或设置一个值该值指示线程的调度优先级。

代码

 static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
        Thread thread1 = new Thread(KobThread);
        thread1.Name = "KobThread";
        thread1.Priority = ThreadPriority.Lowest;
        thread1.Start();
        for(int i = 0; i < 1000; i++) {
            Console.Write("Y");
        }
        Console.Read();
    }
    static void LudwigThread() {
        for(int i = 0; i < 1000; i++) {
            Console.Write("X");
        }
    }

    static void KobThread() {
        for(int i = 0; i < 1000; i++) {
            Console.Write("Z");
        }
    }

打印

有打印可以看出所谓优先级并不是说优先级高的线程执行结束之后才执行优先级低的

 IsAlive

获取一个值该值指示当前线程的执行状态。

代码

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Console.WriteLine("mainThread: " + thread.IsAlive);
        thread.Suspend();
        Console.WriteLine("mainThread: " + thread.IsAlive);
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread: " + Thread.CurrentThread.Name);
    }

打印

IsBackground

获取或设置一个值该值指示某个线程是否为后台线程。

注须在线程start之前设置

代码

1.不设置

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        thread.Suspend();
        Console.WriteLine("mainThread: " + thread.IsAlive);
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread: " + Thread.CurrentThread.Name);
    }

打印

不设置IsBackground 后台线程 我们发现主线程结束后程序无法结束 会等待我们开的线程结束后整个程序才能结束

2.设置

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.IsBackground = true;
        thread.Start();
        thread.Suspend();
        Console.WriteLine("mainThread: " + thread.IsAlive);
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread: " + Thread.CurrentThread.Name);
    }

打印

一旦主线程结束后整个程序结束 不管后台线程是否完成

ThreadState

获取一个值该值包含当前线程的状态。

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
        Thread thread1 = new Thread(KobThread);
        thread1.Name = "KobThread";
        thread1.Priority = ThreadPriority.Lowest;
        thread1.Start();
        Console.WriteLine("---------" + thread.ThreadState);
        Console.WriteLine("=========" + thread1.ThreadState);
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
    }

    static void KobThread() {
        Console.WriteLine("KobThread");
    }

打印

(2).其他属性

CurrentContext

获取线程正在其中执行的当前上下文。

上下文这篇博客有介绍 有兴趣可以看一下

C#综合揭秘——细说进程、应用程序域与上下文之间的关系 - 风尘浪子 - 博客园

CurrentCulture

获取或设置当前线程的区域性。

代码

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Start();

        Console.WriteLine("mainThread: " + thread.CurrentCulture);
        Console.Read();
    }
    static void LudwigThread() {
        
    }

打印


CurrentPrincipal

获取或设置线程的当前负责人对基于角色的安全性而言。

解释

CurrentPrincipal 是.NET应用程序表示运行该进程的用户或服务帐户的标识的方式.

它可以包含一个或多个标识,并允许应用程序通过该IsInRole方法检查主体是否在角色中.

.NET中的大多数身份验证库都将验证用户的凭据,并将Thread类上的此静态属性设置为新的主体对象.

不同的线程可以有不同的主体,因为它们可能正在处理来自不同用户的请求(在ASP.NET Web应用程序HttpContext.UserThread.CurrentPrincipal为每个新请求复制)

拓展

  • 主题 -在安全上下文中 主题 是请求访问 对象的 任何实体。这些是通用术语用于表示请求访问的事物和针对其进行请求的事物。当您登录到应用程序时您就是主题而应用程序就是对象。当有人敲门时访客是请求访问的对象而您的家是请求访问的对象。
  • 主体 - 由帐户角色或其他唯一标识符表示的 主题 子集。当我们达到实现细节的级别时主体是我们在访问控制列表中使用的唯一键。它们可能代表人类用户自动化应用程序连接等。
  • 用户 - 主体的 一个子集通常是指操作员。区别随着时间的流逝而变得模糊因为单词“用户”或“用户ID”通常与“帐户”互换。但是当您需要区分 主体 的大类事物和交互性运营商的子集以不确定性方式驱动交易时“用户”是正确的词。

代码

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Start();

        Console.WriteLine("mainThread: " + Thread.CurrentPrincipal);
        Console.Read();
    }
    static void LudwigThread() {
        
    }

打印


CurrentThread

获取当前正在运行的线程。

代码

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Console.WriteLine("mainThread: " + Thread.CurrentThread.Name);
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread: " + Thread.CurrentThread.Name);
    }

打印

CurrentUICulture

获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。

代码

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Console.WriteLine("mainThread: " + thread.CurrentUICulture);
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread: " + Thread.CurrentThread.Name);
    }

打印

ExecutionContext

获取一个 ExecutionContext 对象该对象包含有关当前线程的各种上下文的信息。

IsThreadPoolThread

获取一个值该值指示线程是否属于托管线程池。


ManagedThreadId

获取当前托管线程的唯一标识符。

代码

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Console.WriteLine("mainThread: " + thread.ManagedThreadId);
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread: " + Thread.CurrentThread.Name);
    }

打印

4.方法

(1).常用方法

public void Start()

开始一个线程。

代码

 static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
    }

Thread初始化之后 调用Start表示开始执行线程


public static void Sleep( int millisecondsTimeout )

让线程暂停一段时间。

static void Main(string[] args) {
        Thread thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        Thread.Sleep(10000);
        Console.WriteLine("LudwigThread Wait");
    }

打印

10s之后


public void Abort()

在调用此方法的线程上引发 ThreadAbortException以开始终止此线程的过程。调用此方法通常会终止线程。

1.在Start后调用

 static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
        thread.Abort();
        Console.WriteLine("main");
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        Thread.Sleep(10000);
        Console.WriteLine("LudwigThread Wait");
    }

打印

 可以看到LudwigThread没有执行直接被中止

2.在线程WaitSleepJoin时调用

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
        Console.WriteLine("main");
        Console.Read();
        while(thread.ThreadState == ThreadState.WaitSleepJoin) {
            thread.Abort();
        }
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        Thread.Sleep(10000);
        Console.WriteLine("LudwigThread Wait");
    }

 Abort调用 LudwigThread 线程尚未走完被终止

3.在线程挂起时调用

1.

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();

        Thread.Sleep(10);
        thread.Suspend();
        thread.Abort();
        Console.WriteLine("main");
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        while(true) {
            Console.WriteLine("X");
        }
    }

结果

2.尝试捕获异常

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();

        Thread.Sleep(10);
        thread.Suspend();
        try {
            thread.Abort();
        } catch(Exception ex) {
            //thread.Resume();
        }
        Console.WriteLine("main");
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        while(true) {
            try {
                Console.WriteLine("X");
            } catch(Exception ex) {
                Console.WriteLine(ex);
            }
        }
    }

 没有调用 Resume 线程被阻塞

3.

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();

        Thread.Sleep(10);
        thread.Suspend();
        try {
            thread.Abort();
        } catch(Exception ex) {
            thread.Resume();
        }
        Console.WriteLine("main");
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        while(true) {
            try {
                Console.WriteLine("X");
            } catch(Exception ex) {
                Console.WriteLine(ex);
            }
        }
    }

直到调用 Resume 后才在挂起的线程中引发 ThreadAbortException

 4.线程中捕获中止

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Priority = ThreadPriority.Highest;
        thread.Start();

        Thread.Sleep(10);
        //thread.Suspend();
        thread.Abort();
        Console.WriteLine("main");
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        while(true) {
            try {
                Console.WriteLine("X");
            } catch(Exception ex) {
                Console.WriteLine(ex);
            }
        }
    }

打印


public static void ResetAbort()

取消为当前线程请求的 Abort。

代码

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Thread.Sleep(10);
        thread.Abort();
        Console.WriteLine("main");
        Console.Read();
    }
    static bool reset = true;
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        while(reset) {
            try {
                Console.WriteLine("X");
            } catch(Exception ex) {
                //Thread.ResetAbort();
                reset = false;
            }
        }
        Console.WriteLine("end");
    }

打印

 我们发现end没有被打印出来

尝试调用ResetAbort()

代码

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Thread.Sleep(10);
        thread.Abort();
        Console.WriteLine("main");
        Console.Read();
    }
    static bool reset = true;
    static void LudwigThread() {
        Console.WriteLine("LudwigThread");
        while(reset) {
            try {
                Console.WriteLine("X");
            } catch(Exception ex) {
                Thread.ResetAbort();
                reset = false;
            }
        }
        Console.WriteLine("end");
    }

打印

end 被执行了

public void Join()

在继续执行标准的 COM 和 SendMessage 消息泵处理期间阻塞调用线程直到某个线程终止为止。此方法有不同的重载形式。

首先我们看下没有join的代码

代码

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        for(int i = 0; i < 1000; i++) {
            Console.Write("Y");
        }
        Console.Read();
    }
    static bool reset = true;
    static void LudwigThread() {

        for(int i = 0; i < 1000; i++) {
            Console.Write("X");
        }
    }

打印

XY无序打印

调用Join

代码

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        thread.Join();
        for(int i = 0; i < 1000; i++) {
            Console.Write("Y");
        }
        Console.Read();
    }
    static bool reset = true;
    static void LudwigThread() {

        for(int i = 0; i < 1000; i++) {
            Console.Write("X");
        }
    }

打印

 Join阻塞了其他线程只有当前线程结束之后才允许调用其他线程


public void Interrupt()

中断处于 WaitSleepJoin 线程状态的线程。

首先还是先看不使用时的代码

代码

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Thread.Sleep(100);
        //thread.Interrupt();
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("Start");
        try {
            Thread.Sleep(10000);
        } catch(Exception ex) {
            Console.WriteLine(ex);
        }
        Console.WriteLine("End");
    }

打印

 先打印Start 10s之后打印End

然后我们使用Interrupt()

代码

static Thread thread;
    static void Main(string[] args) {
        thread = new Thread(LudwigThread);
        thread.Name = "LudwigThread";
        thread.Start();
        Thread.Sleep(100);
        thread.Interrupt();
        Console.Read();
    }
    static void LudwigThread() {
        Console.WriteLine("Start");
        Thread.Sleep(10000);
        Console.WriteLine("End");
    }

 打印

抛出异常并且直接打印出了End

(2).其他方法

public static LocalDataStoreSlot AllocateDataSlot()
在所有的线程上分配未命名的数据槽。为了获得更好的性能请改用以 ThreadStaticAttribute 属性标记的字段。


public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
在所有线程上分配已命名的数据槽。为了获得更好的性能请改用以 ThreadStaticAttribute 属性标记的字段。


public static void BeginCriticalRegion()
通知主机执行将要进入一个代码区域在该代码区域内线程中止或未经处理的异常的影响可能会危害应用程序域中的其他任务。


public static void BeginThreadAffinity()
通知主机托管代码将要执行依赖于当前物理操作系统线程的标识的指令。


public static void EndCriticalRegion()
通知主机执行将要进入一个代码区域在该代码区域内线程中止或未经处理的异常仅影响当前任务。


public static void EndThreadAffinity()
通知主机托管代码已执行完依赖于当前物理操作系统线程的标识的指令。


public static void FreeNamedDataSlot(string name)
为进程中的所有线程消除名称与槽之间的关联。为了获得更好的性能请改用以 ThreadStaticAttribute 属性标记的字段。


public static Object GetData( LocalDataStoreSlot slot )
在当前线程的当前域中从当前线程上指定的槽中检索值。为了获得更好的性能请改用以 ThreadStaticAttribute 属性标记的字段。


public static AppDomain GetDomain()
返回当前线程正在其中运行的当前域。


public static AppDomain GetDomainID()
返回唯一的应用程序域标识符。


public static LocalDataStoreSlot GetNamedDataSlot( string name )
查找已命名的数据槽。为了获得更好的性能请改用以 ThreadStaticAttribute 属性标记的字段。


public static void MemoryBarrier()
按如下方式同步内存存取执行当前线程的处理器在对指令重新排序时不能采用先执行 MemoryBarrier 调用之后的内存存取再执行 MemoryBarrier 调用之前的内存存取的方式。


public static void SetData( LocalDataStoreSlot slot, Object data )
在当前正在运行的线程上为此线程的当前域在指定槽中设置数据。为了获得更好的性能请改用以 ThreadStaticAttribute 属性标记的字段。


public static void SpinWait( int iterations )
导致线程等待由 iterations 参数定义的时间量。


public static byte VolatileRead( ref byte address )
public static double VolatileRead( ref double address )
public static int VolatileRead( ref int address )
public static Object VolatileRead( ref Object address )
读取字段值。无论处理器的数目或处理器缓存的状态如何该值都是由计算机的任何处理器写入的最新值。此方法有不同的重载形式。这里只给出了一些形式。


public static void VolatileWrite( ref byte address, byte value )
public static void VolatileWrite( ref double address, double value )
public static void VolatileWrite( ref int address, int value )
public static void VolatileWrite( ref Object address, Object value )
立即向字段写入一个值以使该值对计算机中的所有处理器都可见。此方法有不同的重载形式。这里只给出了一些形式。


public static bool Yield()
导致调用线程执行准备好在当前处理器上运行的另一个线程。由操作系统选择要执行的线程。 

最后

有些属性和方法没有添加代码和运行结果 有些引用了一些大佬的博客想要完全理解Thread的同学一定要耐下性子仔细阅读有些需要使用不求甚解的同学也要把几个主要的属性和方法看一遍不管是怎样 一定是先理解了什么是线程 他的工作原理是什么然后再学习其方法和属性最后最后一定要自己写一遍几个常用的方法和属性才能真的记得住。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6