线程基础主要回顾线程创设、挂起、等待和停息线程

目录

  • 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. 线程终止时,CLEvoque会保障本人内部的数据结构不会破坏,可是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。CLWrangler为投机保留了IdleTime-Critical优先级,程序中不得设置。

以身作则代码如下所示。

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

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

    // 第二次测试,在单个核心上运行
    Console.WriteLine("rn运行在单个核心上");
    // 设置在单个核心上运行
    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 前台线程和后台线程

在CL奥迪Q5中,线程要么是前台线程,要么正是后台线程。当二个进度的具备前台线程停止运转时,CLOdyssey将强制甘休仍在运转的其余后台线程,不会抛出极度。

在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}");
        }
    }
}

运转结果如下图所示。当前台线程拾伍回巡回甘休之后,成立的后台线程和线程池线程都会被CLLX570强制甘休。

图片 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》这本书的布局,一共更新十贰个章节,先立个Flag。


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

小编水平有限,假设不当招待各位批评指正!

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website