【C#进阶】C# 多线程

这篇具有很好参考价值的文章主要介绍了【C#进阶】C# 多线程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

序号 系列文章
19 【C#进阶】C# 集合类
20 【C#进阶】C# 泛型
21 【C#进阶】C# 匿名方法

前言

🐪 hello大家好,我是哈桑c,本文为大家介绍 C# 中的多线程。


1、线程与多线程的基本概念

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

多线程是指在软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,进而提升整体处理性能的能力。

在 C# 中,实现多线程技术最常使用的类就是包含在 System.Threading 命名空间中的 Thread 类。Thread 类是一个定义创建和控制线程,设置其优先级并获取其状态的类。以一个简单的程序演示 Thread 类的用法,借此讨论多线程的使用。

代码示例:

using System;
using System.Threading;

// 简单的线程场景:启动一个静态方法运行在第二个线程上。
public class ThreadExample
{
    // 被运行的子线程
    public static void ThreadProc()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("子线程: {0}", i);
            Thread.Sleep(0);
        }
    }

    public static void Main()
    {
        Console.WriteLine("主线程:开启第二个线程。");
        
        Thread t = new Thread(new ThreadStart(ThreadProc));
        t.Start();      // 开启子线程的工作
        
        // 同时开启主线程的工作
        for (int i = 0; i < 4; i++)
        {
            Console.WriteLine("主线程:开始工作");
            Thread.Sleep(0);
        }

        Console.WriteLine("主线程:调用Join(),等待子线程结束。");
        t.Join();
        Console.WriteLine("主线程:子线程已经 Join 回来了。按Enter键结束程序。");
        Console.ReadLine();
    }
}

运行结果:
【C#进阶】C# 多线程
在上例中可以看出,使用 Thread 类我们不仅可以执行主线程3的内容,还可以创建新的 Thread 对象以此来运行子线程4的内容。这样我们就成功实现了一个多线程程序。

2、创建并使用线程

在 Thread 类中,创建子线程可以通过 Thread t = new Thread(); 调用构造方法的方式来实现。扩展的 Thread 类调用 Start() 方法来开始子线程的执行。

代码示例:

using System;
using System.Diagnostics;
using System.Threading;

public class Example
{
    public static void Main()
    {
        var th = new Thread(ExecuteInForeground);
        th.Start(4500);
        Thread.Sleep(1000);
        Console.WriteLine("主线程 ({0}) 等待...",
                          Thread.CurrentThread.ManagedThreadId);
    }

    private static void ExecuteInForeground(Object obj)
    {
        int interval;
        try
        {
            interval = (int)obj;
        }
        catch (InvalidCastException)
        {
            interval = 5000;
        }

        var sw = Stopwatch.StartNew();
        Console.WriteLine("线程 {0}: {1}, 属性 {2}",
                          Thread.CurrentThread.ManagedThreadId,
                          Thread.CurrentThread.ThreadState,
                          Thread.CurrentThread.Priority);
        do
        {
            Console.WriteLine("线程 {0}: 运行 {1:N2} 描述",
                              Thread.CurrentThread.ManagedThreadId,
                              sw.ElapsedMilliseconds / 1000.0);
            Thread.Sleep(500);
        } while (sw.ElapsedMilliseconds <= interval);
        sw.Stop();
    }
}

运行结果:
【C#进阶】C# 多线程
在上例中,我们使用 var th = new Thread(ExecuteInForeground); 的方法并传入了 ExecuteInForeground 的构造方法名成功的创建了一个子线程
【C#进阶】C# 多线程
同时我们也可以使用 Thread 类中 start 方法来启动子线程的运行。注意如果方法需要有参数的话,那么这时就需要在 start 方法上传入参数,如果没有则反之。
【C#进阶】C# 多线程

3、检索线程对象

在使用 Thread 类时,如果想要获取线程的信息,可以从正在执行的代码中使用 Thread 类的属性来检索当前正在运行的线程的引用对象

代码示例:

using System;
using System.Threading;

public class ExampleThread
{
    // 创建一个obj对象便于用于锁机制
    static Object obj = new Object();

    public static void Main()
    {
        ThreadPool.QueueUserWorkItem(ShowThreadInformation);    // 将方法排入队列以便执行。 
        var th1 = new Thread(ShowThreadInformation);
        th1.Start();
        var th2 = new Thread(ShowThreadInformation);
        th2.IsBackground = true;
        th2.Start();
        Thread.Sleep(500);
        ShowThreadInformation(null);
    }

    private static void ShowThreadInformation(Object state)
    {
        lock (obj)      // 为保证线程之间的执行顺序,在这里加上一个锁
        {
            var th = Thread.CurrentThread;
            Console.WriteLine("托管线程 #{0}: ", th.ManagedThreadId);
            Console.WriteLine("后台线程: {0}", th.IsBackground);
            Console.WriteLine("线程池线程: {0}", th.IsThreadPoolThread);
            Console.WriteLine("优先级: {0}", th.Priority);
            Console.WriteLine("文化: {0}", th.CurrentCulture.Name);
            Console.WriteLine("UI文化: {0}", th.CurrentUICulture.Name);
            Console.WriteLine();
        }
    }
}

运行结果:
【C#进阶】C# 多线程
从上例中可以看到,我们不仅创建了托管线程,还创建了前台线程、后台线程以及线程池的对象。在这里我们使用了 ManagedThreadId 等属性输出了线程的 Id 值、是否为后台线程或线程池5、优先级和线程名等线程信息。
【C#进阶】C# 多线程

4、前台线程和后台线程

在 .NET 框架的公用语言运行时(Common Language Runtime,CLR)中能区分两种不同类型的线程:前台线程和后台线程。

前台线程和后台线程的区别:

  • 如果所有前台线程已终止,后台线程不会使进程保持运行。
  • 停止所有前台线程后,运行时将停止所有后台线程并关闭。

前台线程和后台线程分别的应用范围。

默认情况下,以下线程在前台执行:

  • 主线程: 常见的主应用程序线程均为前台线程。
  • 子线程: 通过调用类构造函数创建 Thread 的所有线程。

默认情况下,以下线程在后台执行:

  • 线程池线程: 由运行时维护的工作线程池。 可以使用 类配置线程池并计划线程池线程 ThreadPool 上的工作。
  • 非托管到托管的线程: 从非托管代码进入托管执行环境的所有线程。

在 Thread 类中,可以通过设置 IsBackground 的属性来更改在后台执行的线程。后台线程适用于只要应用程序正在运行就应继续执行但不阻止应用程序终止的任何操作,例如监视文件系统更改或传入套接字连接。

代码示例:

using System;
using System.Diagnostics;
using System.Threading;

public class BackgroundThreadExample
{
    public static void Main()
    {
        var th = new Thread(ExecuteInForeground);
        th.IsBackground = true;
        th.Start();
        Thread.Sleep(1000);
        Console.WriteLine("主线程 ({0}) 等待...", 
                        Thread.CurrentThread.ManagedThreadId); 
    }

    private static void ExecuteInForeground()
    {
        var sw = Stopwatch.StartNew();
        Console.WriteLine("线程 {0}: {1}, 优先级 {2}",
                          Thread.CurrentThread.ManagedThreadId,
                          Thread.CurrentThread.ThreadState,
                          Thread.CurrentThread.Priority);
        do
        {
            Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds",
                              Thread.CurrentThread.ManagedThreadId,
                              sw.ElapsedMilliseconds / 1000.0);
            Thread.Sleep(500);
        } while (sw.ElapsedMilliseconds <= 5000);
        sw.Stop();
    }
}

运行结果:
【C#进阶】C# 多线程
从上例中可以看出,我们使用了 Thread 类对象的 IsBackground 属性将 th 线程对象设置为了后台线程。不像前面的示例一样 th 对象可以顺利执行不超过五秒的内容,在上例中可以看到 th 对象只执行了 0.51 秒(并不固定)。这是因为停止所有前台线程后,运行时将停止所有后台线程并关闭。所以在主线程(前台线程)输出"主线程 (1) 等待…"之后就表示主线程停止了,后台线程也会停止并关闭,并不会执行不超过五秒的内容。
【C#进阶】C# 多线程

5、Thread 类的属性和方法

以一个表格展示 Thread 类常用的属性和方法。

Thread 类常用的属性:

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

Thread 类常用的方法:

方法名 描述
Abort() 在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程。
Equals(Object) 确定指定对象是否等于当前对象。(继承自 Object)
Finalize() 确保垃圾回收器回收 Thread 对象时释放资源并执行其他清理操作。
GetApartmentState() 返回表示单元状态的 ApartmentState 值。
GetCompressedStack() 返回 CompressedStack 对象,此对象可用于获取当前线程的堆栈。
GetCurrentProcessorId() 获取用于指示当前线程正在哪个处理器上执行的 ID。
GetDomain() 返回当前线程正在其中运行的当前域。
GetDomainID() 返回唯一的应用程序域标识符。
GetHashCode() 返回当前线程的哈希代码。
GetType() 获取当前实例的 Type。(继承自 Object)
Interrupt() 中断处于 WaitSleepJoin 线程状态的线程。
Join() 在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止。
MemberwiseClone() 创建当前 Object 的浅表副本。(继承自 Object)
ResetAbort() 取消当前线程所请求的 Abort(Object)。
Resume() 继续已挂起的线程。
SetApartmentState(ApartmentState) 在线程启动前设置其单元状态。
SetCompressedStack(CompressedStack) 将捕获的 CompressedStack 应用到当前线程。
Sleep(Int32) 将当前线程挂起指定的毫秒数。
SpinWait(Int32) 导致线程等待由 iterations 参数定义的时间量。
Start() 导致操作系统将当前实例的状态更改为 Running。
ToString() 返回表示当前对象的字符串。(继承自 Object)
TrySetApartmentState(ApartmentState) 在线程启动前设置其单元状态。
UnsafeStart() 导致操作系统将当前实例的状态更改为 Running。
VolatileRead(Byte) 向字段中读取值。
VolatileWrite(Byte, Byte) 向字段中写入值。

点击了解更多多线程的使用。


结语

🐆 以上就是 C# 多线程的介绍啦,希望对大家有所帮助。感谢大家的支持。


  1. 进程: 计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础。 ↩︎

  2. 并发: 指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。 ↩︎

  3. 主线程: 进程中第一个被执行的线程称为主线程。 ↩︎

  4. 子线程: 除了主线程外,在 C# 中使用 Thread t = new Thread(); 方式创建的线程均为子线程。 ↩︎

  5. 线程池: 是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。 ↩︎文章来源地址https://www.toymoban.com/news/detail-491160.html

到了这里,关于【C#进阶】C# 多线程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C#进阶】C# 属性

    序号 系列文章 12 【C#基础】C# 文件与IO 13 【C#进阶】C# 特性 14 【C#进阶】C# 反射 🌲 大家好,我是writer桑,本章为大家介绍 C# 中的 属性 。 属性 是 C# 中的重要成员。 属性和字段 1 类似,但属性提供了一种被称为访问器的灵活机制来 读取、写入或计算私有字段的值 。访问器

    2024年02月11日
    浏览(31)
  • 【C#进阶】C# 索引器

    序号 系列文章 13 【C#进阶】C# 特性 14 【C#进阶】C# 反射 15 【C#进阶】C# 属性 🍂 Hello大家好啊,我是作家桑。本文为大家介绍 C# 中的索引器。 索引器 允许类或结构的实例就像数组一样进行索引。无需显式指定类型或实例成员,即可通过运算符[]设置或检索索引值。索引器类

    2024年02月07日
    浏览(29)
  • 【C#进阶】C# 匿名方法

    序号 系列文章 18 【C#进阶】C# 事件 19 【C#进阶】C# 集合类 20 【C#进阶】C# 泛型 📺 hello大家好啊,我是哈桑c,本文为大家介绍 C# 中的匿名方法。 匿名方法 顾名思义就是这类方法的特点是不需要特别去定义函数的名字的。一般我们需要一个函数,但又不想花时间去命名它的时

    2024年02月04日
    浏览(35)
  • 【C#进阶】C# 集合类

    序号 系列文章 16 【C#进阶】C# 索引器 17 【C#进阶】C# 委托 18 【C#进阶】C# 事件 🌄 hello大家好啊,我是哈桑,本章为大家介绍 C# 中的集合类。 集合类 是专门用于数据存储和检索的类。在这些类中实现了对列表、队列、哈希表等数据结构的封装以及对操作数据的支持。当在项

    2024年02月09日
    浏览(29)
  • 【Java】多线程(进阶)

    乐观锁 乐观锁的基本思想是假设在数据的读取和修改过程中不会有其他的线程对其进行修改, ,因此乐观锁不会立即对数据进行加锁,而是在更新数据时检查是否发生了冲突,如果发现冲突(即数据被其他线程修改),则会进行回滚操作,乐观锁通常使用版本号,时间戳等机制来实现 优

    2024年02月10日
    浏览(36)
  • Java之多线程进阶

    目录 一.上节内容复习 1.线程池的实现 2.自定义一个线程池,构造方法的参数及含义 3.线程池的工作原理 4.拒绝策略 5.为什么不推荐系统提供的线程池 二.常见的锁策略 1.乐观锁和悲观锁 2.轻量级锁和重量级锁 3.读写锁和普通互斥锁 4.自旋锁和挂起等待锁 5.可重入锁和不可重入

    2024年02月05日
    浏览(49)
  • Java 进阶(7) 创建线程

    1. 定义Thread类的⼦类,并重写该类的run()⽅法,该run()⽅法的⽅法体就代表了线程需要完成的任务,因此把run()⽅法称为线程执⾏体。 2. 创建Thread⼦类的实例,即创建了线程对象 3. 调⽤线程对象的start()⽅法来启动该线程 示例: 测试: 1. 定义Runnable接⼝的实现类,并重写该接⼝

    2023年04月16日
    浏览(46)
  • Java 进阶(12) 线程通信

    多个线程在处理同⼀个资源,但是处理的动作(线程的任务)却不相同。 为什么要处理线程间通信 多个线程并发执⾏时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成⼀件任务,并且我们希望他们有规律的执⾏, 那么多线程之间需要⼀些协调通信,以此

    2023年04月16日
    浏览(37)
  • 多线程(进阶三:JUC)

    目录 一、Callable接口 1、创建线程的操作 2、编写多线程代码 (1)实现Runnable接口(使用匿名内部类) (2)实现Callable接口(使用匿名内部类) 二、ReentrantLock 1、ReentrantLock和synchronized的区别 2、如何选择使用哪个锁? 三、原子类 四、线程池 五、信号量 Semaphore 代码示例 六、

    2024年02月20日
    浏览(33)
  • 【多线程进阶】synchronized 原理

    在前面章节中, 提到了多线程中的锁策略, 那么我们 Java 中的锁 synchronized 背后都采取了哪些锁策略呢? 又是如何进行工作的呢? 本节我们就来谈一谈. 关注收藏, 开始学习吧🧐 在 Java 中, synchronized 具有以下特性(这里以 JDK 1.8 为例): 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲

    2024年02月07日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包