线程基础主要包括线程创建、金沙网址挂起、等待和终止线程

目录

  • C#十二线程编制程序种类(二卡塔尔-
    线程底蕴

    • 1.1
      简介
    • 1.2
      创设线程
    • 1.3
      暂停线程
    • 1.4
      线程等待
    • 1.5
      终止线程
    • 1.6
      质量评定线程状态
    • 1.7
      线程优先级
    • 1.8
      前台线程和后台线程
    • 1.9
      向线程传递参数
    • 1.10 C#
      Lock关键字的行使
    • 1.11
      使用Monitor类锁定能源
    • 1.12
      三十二线程中拍卖特别
  • 参照书籍
  • 小编水平有限,假若不当款待各位商议指正!

C#多线程编制程序连串(二卡塔尔国- 线程根基


1.1 简介

线程底蕴主要不外乎线程成立、挂起、等待和停止线程。关于越来越多的线程的平底完毕,CPU时间片轮转等等的文化,能够参照他事他说加以侦察《深入理解计算机系统》大器晚成书中有关进度和线程的章节,本文但是多废话。

1.2 创设线程

在C#言语中,制造线程是生机勃勃件特别轻松的作业;它只要求用到
System.Threading命名空间,在那之中注重运用Thread类来成立线程。

示范代码如下所示:

using System;
using System.Threading; // 创建线程需要用到的命名空间
namespace Recipe1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1.创建一个线程 PrintNumbers为该线程所需要执行的方法
            Thread t = new Thread(PrintNumbers);
            // 2.启动线程
            t.Start();

            // 主线程也运行PrintNumbers方法,方便对照
            PrintNumbers();
            // 暂停一下
            Console.ReadKey();
        }

        static void PrintNumbers()
        {
            // 使用Thread.CurrentThread.ManagedThreadId 可以获取当前运行线程的唯一标识,通过它来区别线程
            Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 开始打印...");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 打印:{i}");
            }
        }
    }
}

运维结果如下图所示,我们能够经过运维结果查出上边的代码创制了贰个线程,然后主线程和创办的线程交叉输出结果,那表达PrintNumbers措施同有的时候间运行在主线程和此外二个线程中。

金沙网址 1

1.3 暂停线程

停顿线程这里运用的方式是由此Thread.Sleep方式,假设线程实行Thread.Sleep方法,那么操作系统就要钦赐的日子内不为该线程分配任曾几何时间片。倘使Sleep时间100ms那么操作系统将起码让该线程睡眠100ms也许更加长日子,所以Thread.Sleep方法不能用作高精度的沙漏使用。

示范代码如下所示:

using System;
using System.Threading; // 创建线程需要用到的命名空间
namespace Recipe2
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1.创建一个线程 PrintNumbers为该线程所需要执行的方法
            Thread t = new Thread(PrintNumbersWithDelay);
            // 2.启动线程
            t.Start();

            // 暂停一下
            Console.ReadKey();
        }

        static void PrintNumbersWithDelay()
        {
            Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 开始打印... 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}");
            for (int i = 0; i < 10; i++)
            {
                //3. 使用Thread.Sleep方法来使当前线程睡眠,TimeSpan.FromSeconds(2)表示时间为 2秒
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 打印:{i} 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}");
            }
        }
    }
}

运作结果如下图所示,通过下图能够分明上边包车型地铁代码是有效的,通过Thread.Sleep主意,使线程休眠了2秒左右,然而实际不是特意纯粹的2秒。验证了地点的说法,它的睡觉是起码让线程睡眠多久,并非迟早多久。

金沙网址 2

1.4 线程等待

在本章中,线程等待使用的是Join方法,该方法将中断实践当前线程,直到所等待的另叁个线程终止。在简要的线程同步中会使用到,但它比较容易,不作过多介绍。

身体力行代码如下所示:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"-------开始执行 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}-------");

        // 1.创建一个线程 PrintNumbersWithDelay为该线程所需要执行的方法
        Thread t = new Thread(PrintNumbersWithDelay);
        // 2.启动线程
        t.Start();
        // 3.等待线程结束
        t.Join();

        Console.WriteLine($"-------执行完毕 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}-------");
        // 暂停一下
        Console.ReadKey();
    }

    static void PrintNumbersWithDelay()
    {
        Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 开始打印... 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}");
        for (int i = 0; i < 10; i++)
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 打印:{i} 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}");
        }
    }
}

运作结果如下图所示,开首奉行和实践完结两条音讯由主线程打字与印刷;根据其出口的风姿浪漫生机勃勃可以预知主线程是等待此外的线程甘休后才输出实行完结那条音讯。

金沙网址 3

1.5 终止线程

悬停线程使用的艺术是Abort措施,当该措施被实行时,将尝试销毁该线程。通过吸引ThreadAbortException可怜使线程被销毁。但平日不推荐使用该方法,原因有以下几点。

  1. 使用Abort办法只是尝尝销毁该线程,但不肯定能止住线程。
  2. 黄金年代旦被终止的线程在施行lock内的代码,那么终止线程会导致线程不安全。
  3. 线程终止时,CLLX570会有限援助本人内部的数据结构不会破坏,可是BCL不可能作保。

据书上说上述原因不推荐使用Abort艺术,在骨子里项目中平常选取CancellationToken来终止线程。

示范代码如下所示:

static void Main(string[] args)
{
    Console.WriteLine($"-------开始执行 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}-------");

    // 1.创建一个线程 PrintNumbersWithDelay为该线程所需要执行的方法
    Thread t = new Thread(PrintNumbersWithDelay);
    // 2.启动线程
    t.Start();
    // 3.主线程休眠6秒
    Thread.Sleep(TimeSpan.FromSeconds(6));
    // 4.终止线程
    t.Abort();

    Console.WriteLine($"-------执行完毕 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}-------");
    // 暂停一下
    Console.ReadKey();
}

static void PrintNumbersWithDelay()
{
    Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 开始打印... 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}");
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(TimeSpan.FromSeconds(2));
        Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 打印:{i} 现在时间{DateTime.Now.ToString("HH:mm:ss.ffff")}");
    }
}

运作结果如下图所示,运行所创办的线程3后,6分钟主线程调用了Abort措施,线程3尚无继续推行便停止了;与预期的结果风姿罗曼蒂克致。

金沙网址 4

1.6 检查测量检验线程状态

线程的意况可由此访谈ThreadState品质来检查实验,ThreadState是三个枚举类型,风度翩翩共有10种意况,状态具体意思如下表所示。

成员名称 说明
Aborted 线程处于 Stopped 状态中。
AbortRequested 已对线程调用了 Thread.Abort 方法,但线程尚未收到试图终止它的挂起的 System.Threading.ThreadAbortException
Background 线程正作为后台线程执行(相对于前台线程而言)。此状态可以通过设置 Thread.IsBackground 属性来控制。
Running 线程已启动,它未被阻塞,并且没有挂起的 ThreadAbortException
Stopped 线程已停止。
StopRequested 正在请求线程停止。这仅用于内部。
Suspended 线程已挂起。
SuspendRequested 正在请求线程挂起。
Unstarted 尚未对线程调用 Thread.Start 方法。
WaitSleepJoin 由于调用 WaitSleepJoin,线程已被阻止。

下表列出以致景况修正的操作。

操作 ThreadState
在公共语言运行库中创建线程。 Unstarted
线程调用 Start Unstarted
线程开始运行。 Running
线程调用 Sleep WaitSleepJoin
线程对其他对象调用 Wait WaitSleepJoin
线程对其他线程调用 Join WaitSleepJoin
另一个线程调用 Interrupt Running
另一个线程调用 Suspend SuspendRequested
线程响应 Suspend 请求。 Suspended
另一个线程调用 Resume Running
另一个线程调用 Abort AbortRequested
线程响应 Abort 请求。 Stopped
线程被终止。 Stopped

演示代码如下所示:

static void Main(string[] args)
{
    Console.WriteLine("开始执行...");

    Thread t = new Thread(PrintNumbersWithStatus);
    Thread t2 = new Thread(DoNothing);

    // 使用ThreadState查看线程状态 此时线程未启动,应为Unstarted
    Console.WriteLine($"Check 1 :{t.ThreadState}");

    t2.Start();
    t.Start();

    // 线程启动, 状态应为 Running
    Console.WriteLine($"Check 2 :{t.ThreadState}");

    // 由于PrintNumberWithStatus方法开始执行,状态为Running
    // 但是经接着会执行Thread.Sleep方法 状态会转为 WaitSleepJoin
    for (int i = 1; i < 30; i++)
    {
        Console.WriteLine($"Check 3 : {t.ThreadState}");
    }

    // 延时一段时间,方便查看状态
    Thread.Sleep(TimeSpan.FromSeconds(6));

    // 终止线程
    t.Abort();

    Console.WriteLine("t线程被终止");

    // 由于该线程是被Abort方法终止 所以状态为 Aborted或AbortRequested
    Console.WriteLine($"Check 4 : {t.ThreadState}");
    // 该线程正常执行结束 所以状态为Stopped
    Console.WriteLine($"Check 5 : {t2.ThreadState}");

    Console.ReadKey();
}

static void DoNothing()
{
    Thread.Sleep(TimeSpan.FromSeconds(2));
}

static void PrintNumbersWithStatus()
{
    Console.WriteLine("t线程开始执行...");

    // 在线程内部,可通过Thread.CurrentThread拿到当前线程Thread对象
    Console.WriteLine($"Check 6 : {Thread.CurrentThread.ThreadState}");
    for (int i = 1; i < 10; i++)
    {
        Thread.Sleep(TimeSpan.FromSeconds(2));
        Console.WriteLine($"t线程输出 :{i}");
    }
}

运营结果如下图所示,与预期的结果意气风发致。

金沙网址 5

1.7 线程优先级

Windows操作系统为抢占式十二线程(Preemptive
multithreaded)操作系统,是因为线程可在其余时间结束(被枪占卡塔 尔(阿拉伯语:قطر‎并调整另一个线程。

Windows操作系统中线程有0(最低) ~ 31(最高)的优先级,而优先级越高所能占用的CPU时间就越来越多,鲜明有个别线程所处的事先级须求思索进程优先级对峙线程优先级三个优先级。

  1. 进程优先级:Windows辅助6个进度优先级,分别是Idle、Below Normal、Normal、Above normal、High 和Realtime。默认为Normal
  2. 相对线程优先级:相对线程优先级是争执于经过优先级的,因为经过包括了线程。Windows帮助7个绝对线程优先级,分别是Idle、Lowest、Below Normal、Normal、Above Normal、Highest 和 Time-Critical.默认为Normal

下表总括了进程的预先级线程的相对优先级优先级(0~31)的映射关系。粗体为相对线程优先级,斜体为经过优先级

Idle Below Normal Normal Above Normal High Realtime
Time-Critical 15 15 15 15 15 31
Highest 6 8 10 12 15 26
Above Normal 5 7 9 11 14 25
Normal 4 6 8 10 13 24
Below Normal 3 5 7 9 12 23
Lowest 2 4 6 8 11 22
Idle 1 1 1 1 1 16

而在C#程序中,可改进线程的相对优先级,须求安装ThreadPriority属性,可安装为ThreadPriority枚举类型的多少个值之后生可畏:Lowest、BelowNormal、Normal、AboveNormal 或 Highest。CLEscort为协和保留了IdleTime-Critical优先级,程序中不得设置。

以身作则代码如下所示。

static void Main(string[] args)
{
    Console.WriteLine($"当前线程优先级: {Thread.CurrentThread.Priority} \r\n");

    // 第一次测试,在所有核心上运行
    Console.WriteLine("运行在所有空闲的核心上");
    RunThreads();
    Thread.Sleep(TimeSpan.FromSeconds(2));

    // 第二次测试,在单个核心上运行
    Console.WriteLine("\r\n运行在单个核心上");
    // 设置在单个核心上运行
    System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
    RunThreads();

    Console.ReadLine();
}

static void RunThreads()
{
    var sample = new ThreadSample();

    var threadOne = new Thread(sample.CountNumbers);
    threadOne.Name = "线程一";
    var threadTwo = new Thread(sample.CountNumbers);
    threadTwo.Name = "线程二";

    // 设置优先级和启动线程
    threadOne.Priority = ThreadPriority.Highest;
    threadTwo.Priority = ThreadPriority.Lowest;
    threadOne.Start();
    threadTwo.Start();

    // 延时2秒 查看结果
    Thread.Sleep(TimeSpan.FromSeconds(2));
    sample.Stop();
}

class ThreadSample
{
    private bool _isStopped = false;

    public void Stop()
    {
        _isStopped = true;
    }

    public void CountNumbers()
    {
        long counter = 0;

        while (!_isStopped)
        {
            counter++;
        }

        Console.WriteLine($"{Thread.CurrentThread.Name} 优先级为 {Thread.CurrentThread.Priority,11} 计数为 = {counter,13:N0}");
    }
}

运行结果如下图所示。Highest占有的CPU时间显著多于Lowest。当程序运维在具备骨干上时,线程能够在不一致大旨同期运营,所以HighestLowest差异会小片段。

金沙网址 6

1.8 前台线程和后台线程

在CLTiguan中,线程要么是前台线程,要么便是后台线程。当叁个历程的持有前台线程结束运作时,CLR将强制甘休仍在运作的任何后台线程,不会抛出非凡。

在C#中可由此Thread类中的IsBackground个性来钦点是或不是为后台线程。在线程生命周期中,任曾几何时候都可以前台线程变为后台线程。线程池中的线程暗中同意为后台线程

演示代码如下所示。

static void Main(string[] args)
{
    var sampleForeground = new ThreadSample(10);
    var sampleBackground = new ThreadSample(20);
    var threadPoolBackground = new ThreadSample(20);

    // 默认创建为前台线程
    var threadOne = new Thread(sampleForeground.CountNumbers);
    threadOne.Name = "前台线程";

    var threadTwo = new Thread(sampleBackground.CountNumbers);
    threadTwo.Name = "后台线程";
    // 设置IsBackground属性为 true 表示后台线程
    threadTwo.IsBackground = true;

    // 线程池内的线程默认为 后台线程
    ThreadPool.QueueUserWorkItem((obj) => {
        Thread.CurrentThread.Name = "线程池线程";
        threadPoolBackground.CountNumbers();
    });

    // 启动线程 
    threadOne.Start();
    threadTwo.Start();
}

class ThreadSample
{
    private readonly int _iterations;

    public ThreadSample(int iterations)
    {
        _iterations = iterations;
    }
    public void CountNumbers()
    {
        for (int i = 0; i < _iterations; i++)
        {
            Thread.Sleep(TimeSpan.FromSeconds(0.5));
            Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
        }
    }
}

运作结果如下图所示。当前台线程11回巡回截止之后,创制的后台线程和线程池线程都会被CLLacrosse强制截至。

金沙网址 7

1.9 向线程传递参数

向线程中传送参数常用的有三种艺术,构造函数传值、Start方法传值和Lambda表明式传值,通日常用Start方法来传值。

亲自过问代码如下所示,通过二种方法来传递参数,告诉线程中的循环最后要求循环几回。

static void Main(string[] args)
{
    // 第一种方法 通过构造函数传值
    var sample = new ThreadSample(10);

    var threadOne = new Thread(sample.CountNumbers);
    threadOne.Name = "ThreadOne";
    threadOne.Start();
    threadOne.Join();

    Console.WriteLine("--------------------------");

    // 第二种方法 使用Start方法传值 
    // Count方法 接收一个Object类型参数
    var threadTwo = new Thread(Count);
    threadTwo.Name = "ThreadTwo";
    // Start方法中传入的值 会传递到 Count方法 Object参数上
    threadTwo.Start(8);
    threadTwo.Join();

    Console.WriteLine("--------------------------");

    // 第三种方法 Lambda表达式传值
    // 实际上是构建了一个匿名函数 通过函数闭包来传值
    var threadThree = new Thread(() => CountNumbers(12));
    threadThree.Name = "ThreadThree";
    threadThree.Start();
    threadThree.Join();
    Console.WriteLine("--------------------------");

    // Lambda表达式传值 会共享变量值
    int i = 10;
    var threadFour = new Thread(() => PrintNumber(i));
    i = 20;
    var threadFive = new Thread(() => PrintNumber(i));
    threadFour.Start();
    threadFive.Start();
}

static void Count(object iterations)
{
    CountNumbers((int)iterations);
}

static void CountNumbers(int iterations)
{
    for (int i = 1; i <= iterations; i++)
    {
        Thread.Sleep(TimeSpan.FromSeconds(0.5));
        Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
    }
}

static void PrintNumber(int number)
{
    Console.WriteLine(number);
}

class ThreadSample
{
    private readonly int _iterations;

    public ThreadSample(int iterations)
    {
        _iterations = iterations;
    }
    public void CountNumbers()
    {
        for (int i = 1; i <= _iterations; i++)
        {
            Thread.Sleep(TimeSpan.FromSeconds(0.5));
            Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
        }
    }
}

运作结果如下图所示,与预期结果相符。

金沙网址 8

1.10 C# Lock关键字的利用

在四十五二十四线程的种类中,由于CPU的时辰片轮转等线程调治算法的行使,轻便并发线程安全难题。具体可参照《深入理解计算机系统》大器晚成书相关的章节。

在C#中lock关键字是一个语法糖,它将Monitor装进,给object加上多少个互斥锁,进而实今世码的线程安全,Monitor会在下风流罗曼蒂克节中牵线。

对于lock第一字依旧Monitor锁定的对象,都必得小心选拔,不赶巧的选拔或者会促成深重的性申斥题依旧发出死锁。以下有几条有关选择锁定目的的提出。

  1. 联手锁定的靶子无法是值类型。因为使用值类型时会有装箱的主题素材,装箱后的就成了一个新的实例,会促成Monitor.Enter()Monitor.Exit()收下到分裂的实例而错过关联性
  2. 制止锁定this、typeof(type)和stringthistypeof(type)锁定或然在别的不相干的代码中会有相符的定义,引致四个协作块互相堵塞。string要求酌量字符串拘禁的难点,假若同一个字符串常量在三个地点现身,恐怕援用的会是同四个实例。
  3. 目的的抉择功效域尽恐怕恰恰达到供给,使用静态的、私有的变量。

以下演示代码实现了二十四线程情况下的计数功用,风姿罗曼蒂克种完结是线程不安全的,会促成结果与预期不切合,但也是有希望正确。此外豆蔻梢头种选拔了lock第一字张开线程同步,所以它结果是无可否认的。

static void Main(string[] args)
{
    Console.WriteLine("错误的多线程计数方式");

    var c = new Counter();
    // 开启3个线程,使用没有同步块的计数方式对其进行计数
    var t1 = new Thread(() => TestCounter(c));
    var t2 = new Thread(() => TestCounter(c));
    var t3 = new Thread(() => TestCounter(c));
    t1.Start();
    t2.Start();
    t3.Start();
    t1.Join();
    t2.Join();
    t3.Join();

    // 因为多线程 线程抢占等原因 其结果是不一定的  碰巧可能为0
    Console.WriteLine($"Total count: {c.Count}");
    Console.WriteLine("--------------------------");

    Console.WriteLine("正确的多线程计数方式");

    var c1 = new CounterWithLock();
    // 开启3个线程,使用带有lock同步块的方式对其进行计数
    t1 = new Thread(() => TestCounter(c1));
    t2 = new Thread(() => TestCounter(c1));
    t3 = new Thread(() => TestCounter(c1));
    t1.Start();
    t2.Start();
    t3.Start();
    t1.Join();
    t2.Join();
    t3.Join();

    // 其结果是一定的 为0
    Console.WriteLine($"Total count: {c1.Count}");

    Console.ReadLine();
}

static void TestCounter(CounterBase c)
{
    for (int i = 0; i < 100000; i++)
    {
        c.Increment();
        c.Decrement();
    }
}

// 线程不安全的计数
class Counter : CounterBase
{
    public int Count { get; private set; }

    public override void Increment()
    {
        Count++;
    }

    public override void Decrement()
    {
        Count--;
    }
}

// 线程安全的计数
class CounterWithLock : CounterBase
{
    private readonly object _syncRoot = new Object();

    public int Count { get; private set; }

    public override void Increment()
    {
        // 使用Lock关键字 锁定私有变量
        lock (_syncRoot)
        {
            // 同步块
            Count++;
        }
    }

    public override void Decrement()
    {
        lock (_syncRoot)
        {
            Count--;
        }
    }
}

abstract class CounterBase
{
    public abstract void Increment();

    public abstract void Decrement();
}

运营结果如下图所示,与预期结果符合。

金沙网址 9

1.11 使用Monitor类锁定财富

Monitor类首要用于线程同步中,
lock主要字是对Monitor类的多个装进,其卷入结构如下代码所示。

try
{
    Monitor.Enter(obj);
    dosomething();
}
catch(Exception ex)
{  
}
finally
{
    Monitor.Exit(obj);
}

以下代码演示了选拔Monitor.TyeEnter()措施防止资源死锁和利用lock爆发产资料源死锁的景观。

        static void Main(string[] args)
        {
            object lock1 = new object();
            object lock2 = new object();

            new Thread(() => LockTooMuch(lock1, lock2)).Start();

            lock (lock2)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Monitor.TryEnter可以不被阻塞, 在超过指定时间后返回false");
                // 如果5S不能进入同步块,那么返回。
                // 因为前面的lock锁定了 lock2变量  而LockTooMuch()一开始锁定了lock1 所以这个同步块无法获取 lock1 而LockTooMuch方法内也不能获取lock2
                // 只能等待TryEnter超时 释放 lock2 LockTooMuch()才会是释放 lock1
                if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
                {
                    Console.WriteLine("获取保护资源成功");
                }
                else
                {
                    Console.WriteLine("获取资源超时");
                }
            }

            new Thread(() => LockTooMuch(lock1, lock2)).Start();

            Console.WriteLine("----------------------------------");
            lock (lock2)
            {
                Console.WriteLine("这里会发生资源死锁");
                Thread.Sleep(1000);
                // 这里必然会发生死锁  
                // 本同步块 锁定了 lock2 无法得到 lock1
                // 而 LockTooMuch 锁定了 lock1 无法得到 lock2
                lock (lock1)
                {
                    // 该语句永远都不会执行
                    Console.WriteLine("获取保护资源成功");
                }
            }
        }

        static void LockTooMuch(object lock1, object lock2)
        {
            lock (lock1)
            {
                Thread.Sleep(1000);
                lock (lock2) ;
            }
        }

运作结果如下图所示,因为运用Monitor.TryEnter()方式在逾期过后会回去,不会窒碍线程,所以并未有生出死锁。而第二段代码中lock从没过期再次回到的机能,导致能源死锁,同步块中的代码永久不会被奉行。

金沙网址 10

1.12 四线程中拍卖极其

在多线程中拍卖非常应当选用前后原则,在哪些线程发生极其那么所在的代码块肯定要有对应的拾叁分管理。不然大概会招致程序崩溃、数据错过。

主线程中央银行使try/catch言语是不可能捕获创立线程中的至极。可是万朝气蓬勃遭受不可预料的要命,可经过监听AppDomain.CurrentDomain.UnhandledException事件来拓宽捕获和极其管理。

演示代码如下所示,极度管理 1 和 至极处理 2 能正常被实践,而至极管理 3
是无效的。

static void Main(string[] args)
{
    // 启动线程,线程代码中进行异常处理
    var t = new Thread(FaultyThread);
    t.Start();
    t.Join();

    // 捕获全局异常
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    t = new Thread(BadFaultyThread);
    t.Start();
    t.Join();

    // 线程代码中不进行异常处理,尝试在主线程中捕获
    AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
    try
    {
        t = new Thread(BadFaultyThread);
        t.Start();
    }
    catch (Exception ex)
    {
        // 永远不会运行
        Console.WriteLine($"异常处理 3 : {ex.Message}");
    }
}

private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    Console.WriteLine($"异常处理 2 :{(e.ExceptionObject as Exception).Message}");
}

static void BadFaultyThread()
{
    Console.WriteLine("有异常的线程已启动...");
    Thread.Sleep(TimeSpan.FromSeconds(2));
    throw new Exception("Boom!");
}

static void FaultyThread()
{
    try
    {
        Console.WriteLine("有异常的线程已启动...");
        Thread.Sleep(TimeSpan.FromSeconds(1));
        throw new Exception("Boom!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"异常处理 1 : {ex.Message}");
    }
}

运营结果如下图所示,与预期结果后生可畏致。

金沙网址 11

参照他事他说加以考查书籍

本文首要参照了以下几本书,在这里对那么些小编表示真诚的多谢你们提供了这样好的材料。

  1. 《CLR via C#》
  2. 《C# in Depth Third Edition》
  3. 《Essential C# 6.0》
  4. 《Multithreading with C# Cookbook Second Edition》

线程底子这风姿洒脱章节好不轻易收拾完了,是作者学习进程中的笔记和思量。布署依据《Multithreading
with C# Cookbook Second
Edition》那本书的构造,风流倜傥共更新10个章节,先立个Flag。


源码下载点击链接
演示源码下载

笔者水平有限,假诺不当迎接各位讨论指正!

相关文章