四线程基础篇3,多线程编制程序体系

目录

八线程(基础篇3),三三十二线程基础篇3

  在上一篇十六线程(基础篇2)中,大家根本描述了鲜明线程的事态、线程优先级、前台线程和后台线程以及向线程传递参数的学问,在这一篇中我们将讲述怎么样使用C#的lock关键字锁定线程、使用Monitor锁定线程以及线程中的至极处理。

九、使用C#的lock关键字锁定线程

一 、使用Visual Studio 二〇一四创办三个新的控制台应用程序。

② 、双击打开“Program.cs”文件,然后修改为如下代码:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 
 5 namespace Recipe09
 6 {
 7     abstract class CounterBase
 8     {
 9         public abstract void Increment();
10         public abstract void Decrement();
11     }
12 
13     class Counter : CounterBase
14     {
15         public int Count { get; private set; }
16 
17         public override void Increment()
18         {
19             Count++;
20         }
21 
22         public override void Decrement()
23         {
24             Count--;
25         }
26     }
27 
28     class CounterWithLock : CounterBase
29     {
30         private readonly object syncRoot = new Object();
31 
32         public int Count { get; private set; }
33 
34         public override void Increment()
35         {
36             lock (syncRoot)
37             {
38                 Count++;
39             }
40         }
41 
42         public override void Decrement()
43         {
44             lock (syncRoot)
45             {
46                 Count--;
47             }
48         }
49     }
50 
51     class Program
52     {
53         static void TestCounter(CounterBase c)
54         {
55             for (int i = 0; i < 100000; i++)
56             {
57                 c.Increment();
58                 c.Decrement();
59             }
60         }
61 
62         static void Main(string[] args)
63         {
64             WriteLine("Incorrect counter");
65             var c1 = new Counter();
66             var t1 = new Thread(() => TestCounter(c1));
67             var t2 = new Thread(() => TestCounter(c1));
68             var t3 = new Thread(() => TestCounter(c1));
69             t1.Start();
70             t2.Start();
71             t3.Start();
72             t1.Join();
73             t2.Join();
74             t3.Join();
75             WriteLine($"Total count: {c1.Count}");
76 
77             WriteLine("--------------------------");
78 
79             WriteLine("Correct counter");
80             var c2 = new CounterWithLock();
81             t1 = new Thread(() => TestCounter(c2));
82             t2 = new Thread(() => TestCounter(c2));
83             t3 = new Thread(() => TestCounter(c2));
84             t1.Start();
85             t2.Start();
86             t3.Start();
87             t1.Join();
88             t2.Join();
89             t3.Join();
90             WriteLine($"Total count: {c2.Count}");
91         }
92     }
93 }

③ 、运营该控制台应用程序,运转作效果果(每趟运维效果或然差异)如下图所示:

亚洲必赢官网 1

  在第六5行代码处,大家创制了Counter类的一个对象,该类定义了一个粗略的counter变量,该变量可以自增1和自减1。然后在第66~68行代码处,大家创立了三个线程,并利用lambda表明式将Counter对象传递给了“TestCounter”方法,那八个线程共享同1个counter变量,并且对这几个变量实行自增和自减操作,这将招致结果的不正确。假设大家反复运作这些控制台程序,它将打字与印刷出分歧的counter值,有可能是0,但大部分地方下不是。

  发生那种景色是因为Counter类是非线程安全的。大家假若第三个线程在第67行代码处执行达成后,还未曾履行第伍8行代码时,第3个线程也实施了第六7行代码,这几个时候counter的变量值自增了一回,然后,那五个线程同时实施了第陆8行处的代码,那会导致counter的变量只自减了贰次,因而,造成了不得法的结果。

  为了保障不产生上述不得法的状态,我们亟须确认保障在某八个线程访问counter变量时,其它全体的线程必须等待其进行完结才能继承走访,大家得以应用lock关键字来形成那么些功用。假若大家在有些线程中锁定三个目的,别的全数线程必须等到该线程解锁之后才能访问到那几个指标,由此,可防止止上述情状的发生。不过要留心的是,使用那种艺术会严重影响程序的品质。更好的方法大家将会在仙童联手中讲述。

10、使用Monitor锁定线程

   在这一小节中,我们将讲述3个多线程编制程序中的常见的一个难点:死锁。大家率先创立2个死锁的言传身教,然后使用Monitor防止死锁的爆发。

① 、使用Visual Studio 二〇一五成立三个新的控制台应用程序。

贰 、双击打开“Program.cs”文件,编写代码如下:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe10
 7 {
 8     class Program
 9     {
10         static void LockTooMuch(object lock1, object lock2)
11         {
12             lock (lock1)
13             {
14                 Sleep(1000);
15                 lock (lock2)
16                 {
17                 }
18             }
19         }
20 
21         static void Main(string[] args)
22         {
23             object lock1 = new object();
24             object lock2 = new object();
25 
26             new Thread(() => LockTooMuch(lock1, lock2)).Start();
27 
28             lock (lock2)
29             {
30                 WriteLine("This will be a deadlock!");
31                 Sleep(1000);
32                 lock (lock1)
33                 {
34                     WriteLine("Acquired a protected resource succesfully");
35                 }
36             }
37         }
38     }
39 }

四线程基础篇3,多线程编制程序体系。③ 、运维该控制台应用程序,运转效果如下图所示:

亚洲必赢官网 2

  在上述结果中大家得以看来程序产生了死锁,程序一向停止不了。

  在第10~19行代码处,大家定义了3个名为“LockTooMuch”的章程,在该方法中我们锁定了第二个目的lock1,等待1分钟后,希望锁定第二个对象lock2。

  在第③6行代码处,我们创设了一个新的线程来进行“LockTooMuch”方法,然后马上施行第③8行代码。

  在第28~32行代码处,大家在主线程中锁定了目的lock2,然后等待1分钟后,希望锁定第②个指标lock1。

  在创立的新线程中我们锁定了对象lock1,等待1分钟,希望锁定目的lock2,而以此时候对象lock2已经被主线程锁定,所以新建线程会等待对象lock2被主线程解锁。不过,在主线程中,大家锁定了对象lock2,等待1分钟,希望锁定目的lock1,而以此时候对象lock1已经被创建的线程锁定,所以主线程会等待对象lock1被创建的线程解锁。当爆发那种景色的时候,死锁就发生了,所以大家的控制台应用程序近年来不能够符合规律甘休。

肆 、要防止死锁的发生,我们得以接纳“Monitor.TryEnter”方法来替换lock关键字,“Monitor.TryEnter”方法在央求不到能源时不会堵塞等待,能够设置超时时间,获取不到直接再次回到false。修改代码如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe10
 7 {
 8     class Program
 9     {
10         static void LockTooMuch(object lock1, object lock2)
11         {
12             lock (lock1)
13             {
14                 Sleep(1000);
15                 lock (lock2)
16                 {
17                 }
18             }
19         }
20 
21         static void Main(string[] args)
22         {
23             object lock1 = new object();
24             object lock2 = new object();
25 
26             new Thread(() => LockTooMuch(lock1, lock2)).Start();
27 
28             lock (lock2)
29             {
30                 WriteLine("This will be a deadlock!");
31                 Sleep(1000);
32                 //lock (lock1)
33                 //{
34                 //    WriteLine("Acquired a protected resource succesfully");
35                 //}
36                 if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
37                 {
38                     WriteLine("Acquired a protected resource succesfully");
39                 }
40                 else
41                 {
42                     WriteLine("Timeout acquiring a resource!");
43                 }
44             }
45         }
46     }
47 }

伍 、运转该控制台应用程序,运转效果如下图所示:

亚洲必赢官网 3

  此时,大家的控制台应用程序就防止了死锁的发生。

十① 、处理万分

   在这一小节中,大家描述怎么样在线程中正确地拍卖万分。正确地将try/catch块放置在线程内部是万分首要的,因为在线程外部捕获线程内部的极度平常是不容许的。

一 、使用Visual Studio 贰零壹肆创设3个新的控制台应用程序。

② 、双击打开“Program.cs”文件,修改代码如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe11
 7 {
 8     class Program
 9     {
10         static void BadFaultyThread()
11         {
12             WriteLine("Starting a faulty thread...");
13             Sleep(TimeSpan.FromSeconds(2));
14             throw new Exception("Boom!");
15         }
16 
17         static void FaultyThread()
18         {
19             try
20             {
21                 WriteLine("Starting a faulty thread...");
22                 Sleep(TimeSpan.FromSeconds(1));
23                 throw new Exception("Boom!");
24             }
25             catch(Exception ex)
26             {
27                 WriteLine($"Exception handled: {ex.Message}");
28             }
29         }
30 
31         static void Main(string[] args)
32         {
33             var t = new Thread(FaultyThread);
34             t.Start();
35             t.Join();
36 
37             try
38             {
39                 t = new Thread(BadFaultyThread);
40                 t.Start();
41             }
42             catch (Exception ex)
43             {
44                 WriteLine(ex.Message);
45                 WriteLine("We won't get here!");
46             }
47         }
48     }
49 }

叁 、运维该控制台应用程序,运转效果如下图所示:

亚洲必赢官网 4

  在第10~15行代码处,大家定义了二个名为“BadFaultyThread”的艺术,在该办法中抛出一个要命,并且没有应用try/catch块捕获该特别。

  在第17~29行代码处,大家定义了2个名为“FaultyThread”的点子,在该形式中也抛出八个杰出,但是大家运用了try/catch块捕获了该尤其。

  在第33~35行代码处,大家创设了三个线程,在该线程中实践了“FaultyThread”方法,我们能够观望在那几个新成立的线程中,我们科学地破获了在“FaultyThread”方法中抛出的更加。

  在第37~46行代码处,大家又新创设了三个线程,在该线程中进行了“BadFaultyThread”方法,并且在主线程中动用try/catch块来捕获在新创制的线程中抛出的那些,不幸的的是大家在主线程中不能够捕获在新线程中抛出的不行。

  因此能够观望,在三个线程中抓获另三个线程中的分外平时是不可行的。

  至此,二十四线程(基础篇)我们就讲述到这儿,之后大家将讲述线程同步相关的学识,敬请期待!

  源码下载

在上一篇二十四线程(基础篇2)中,我们最首要讲述了规定线程的景观、线程优先级、前台线程和后台线程以…

  在上一篇C#八线程之基础篇2中,大家任重先生而道远讲述了规定线程的状态、线程优先级、前台线程和后台线程以及向线程传递参数的学识,在这一篇中大家将讲述怎样使用C#的lock关键字锁定线程、使用Monitor锁定线程以及线程中的至极处理。

  在上一篇C#二十多线程之基础篇2中,大家重视描述了分明线程的意况、线程优先级、前台线程和后台线程以及向线程传递参数的文化,在这一篇中大家将讲述怎么着使用C#的lock关键字锁定线程、使用Monitor锁定线程以及线程中的非凡处理。

  • 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#的lock关键字锁定线程

九、使用C#的lock关键字锁定线程

C#二十八线程编制程序连串(二)- 线程基础


一 、使用Visual Studio 贰零壹陆创造1个新的控制台应用程序。

壹 、使用Visual Studio 2014开立三个新的控制台应用程序。

1.1 简介

线程基础重要包含线程创设、挂起、等待和终止线程。关于更加多的线程的尾部达成,CPU时间片轮转等等的学问,能够参见《深入理解计算机系统》一书中关于进度和线程的章节,本文可是多废话。

贰 、双击打开“Program.cs”文件,然后修改为如下代码:

② 、双击打开“Program.cs”文件,然后修改为如下代码:

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

运维结果如下图所示,大家得以透过运维结果得知上边的代码成立了1个线程,然后主线程和创造的线程交叉输出结果,这评释PrintNumbers主意同时运行在主线程和此外2个线程中。

亚洲必赢官网 5

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 
 5 namespace Recipe09
 6 {
 7     abstract class CounterBase
 8     {
 9         public abstract void Increment();
10         public abstract void Decrement();
11     }
12 
13     class Counter : CounterBase
14     {
15         public int Count { get; private set; }
16 
17         public override void Increment()
18         {
19             Count++;
20         }
21 
22         public override void Decrement()
23         {
24             Count--;
25         }
26     }
27 
28     class CounterWithLock : CounterBase
29     {
30         private readonly object syncRoot = new Object();
31 
32         public int Count { get; private set; }
33 
34         public override void Increment()
35         {
36             lock (syncRoot)
37             {
38                 Count++;
39             }
40         }
41 
42         public override void Decrement()
43         {
44             lock (syncRoot)
45             {
46                 Count--;
47             }
48         }
49     }
50 
51     class Program
52     {
53         static void TestCounter(CounterBase c)
54         {
55             for (int i = 0; i < 100000; i++)
56             {
57                 c.Increment();
58                 c.Decrement();
59             }
60         }
61 
62         static void Main(string[] args)
63         {
64             WriteLine("Incorrect counter");
65             var c1 = new Counter();
66             var t1 = new Thread(() => TestCounter(c1));
67             var t2 = new Thread(() => TestCounter(c1));
68             var t3 = new Thread(() => TestCounter(c1));
69             t1.Start();
70             t2.Start();
71             t3.Start();
72             t1.Join();
73             t2.Join();
74             t3.Join();
75             WriteLine($"Total count: {c1.Count}");
76 
77             WriteLine("--------------------------");
78 
79             WriteLine("Correct counter");
80             var c2 = new CounterWithLock();
81             t1 = new Thread(() => TestCounter(c2));
82             t2 = new Thread(() => TestCounter(c2));
83             t3 = new Thread(() => TestCounter(c2));
84             t1.Start();
85             t2.Start();
86             t3.Start();
87             t1.Join();
88             t2.Join();
89             t3.Join();
90             WriteLine($"Total count: {c2.Count}");
91         }
92     }
93 }
 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 
 5 namespace Recipe09
 6 {
 7     abstract class CounterBase
 8     {
 9         public abstract void Increment();
10         public abstract void Decrement();
11     }
12 
13     class Counter : CounterBase
14     {
15         public int Count { get; private set; }
16 
17         public override void Increment()
18         {
19             Count++;
20         }
21 
22         public override void Decrement()
23         {
24             Count--;
25         }
26     }
27 
28     class CounterWithLock : CounterBase
29     {
30         private readonly object syncRoot = new Object();
31 
32         public int Count { get; private set; }
33 
34         public override void Increment()
35         {
36             lock (syncRoot)
37             {
38                 Count++;
39             }
40         }
41 
42         public override void Decrement()
43         {
44             lock (syncRoot)
45             {
46                 Count--;
47             }
48         }
49     }
50 
51     class Program
52     {
53         static void TestCounter(CounterBase c)
54         {
55             for (int i = 0; i < 100000; i++)
56             {
57                 c.Increment();
58                 c.Decrement();
59             }
60         }
61 
62         static void Main(string[] args)
63         {
64             WriteLine("Incorrect counter");
65             var c1 = new Counter();
66             var t1 = new Thread(() => TestCounter(c1));
67             var t2 = new Thread(() => TestCounter(c1));
68             var t3 = new Thread(() => TestCounter(c1));
69             t1.Start();
70             t2.Start();
71             t3.Start();
72             t1.Join();
73             t2.Join();
74             t3.Join();
75             WriteLine($"Total count: {c1.Count}");
76 
77             WriteLine("--------------------------");
78 
79             WriteLine("Correct counter");
80             var c2 = new CounterWithLock();
81             t1 = new Thread(() => TestCounter(c2));
82             t2 = new Thread(() => TestCounter(c2));
83             t3 = new Thread(() => TestCounter(c2));
84             t1.Start();
85             t2.Start();
86             t3.Start();
87             t1.Join();
88             t2.Join();
89             t3.Join();
90             WriteLine($"Total count: {c2.Count}");
91         }
92     }
93 }

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秒。验证了地方的传道,它的上床是起码让线程睡眠多久,而不是肯定多长期。

亚洲必赢官网 6

③ 、运维该控制台应用程序,运营效果(每便运转作效果果兴许差异)如下图所示:

③ 、运营该控制台应用程序,运维作效果果(每趟运转效果说不定区别)如下图所示:

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

运营结果如下图所示,起始履行和执行完毕两条音讯由主线程打字与印刷;依照其出口的次第可知主线程是等待其余的线程停止后才输出执行达成那条音信。

亚洲必赢官网 7

亚洲必赢官网 8

亚洲必赢官网 9

1.5 终止线程

停下线程使用的艺术是Abort艺术,当该方法被实施时,将尝试销毁该线程。通过抓住ThreadAbortException十一分使线程被销毁。但一般不引进使用该措施,原因有以下几点。

  1. 使用Abort方法只是尝尝销毁该线程,但不自然能终止线程。
  2. 即使被结束的线程在实施lock内的代码,那么终止线程会招致线程不安全。
  3. 线程终止时,CLSportage会保障本人之中的数据结构不会损坏,不过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尚无继续执行便甘休了;与预期的结果一致。

亚洲必赢官网 10

  在第⑥5行代码处,大家创制了Counter类的贰个对象,该类定义了三个简便的counter变量,该变量能够自增1和自减1。然后在第56~68行代码处,大家创制了五个线程,并行使lambda表明式将Counter对象传递给了“TestCounter”方法,那多个线程共享同一个counter变量,并且对那些变量进行自增和自减操作,这将导致结果的不科学。如若大家往往运转那个控制台程序,它将打字与印刷出差异的counter值,有也许是0,但大部分气象下不是。

  在第伍5行代码处,大家成立了Counter类的贰个对象,该类定义了贰个简易的counter变量,该变量能够自增1和自减1。然后在第伍6~68行代码处,我们创设了八个线程,并使用lambda表明式将Counter对象传递给了“TestCounter”方法,那四个线程共享同二个counter变量,并且对那个变量进行自增和自减操作,这将造成结果的不科学。假设大家一再运维这么些控制台程序,它将打字与印刷出分化的counter值,有可能是0,但超越四分之一景观下不是。

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

运行结果如下图所示,与预期的结果一致。

亚洲必赢官网 11

  爆发那种气象是因为Counter类是非线程安全的。我们只要第1个线程在第肆7行代码处执行完结后,还没有举行第68行代码时,第贰个线程也推行了第六7行代码,那几个时候counter的变量值自增了一次,然后,那多个线程同时进行了第⑤8行处的代码,那会造成counter的变量只自减了3回,因而,造成了不科学的结果。

  产生这种状态是因为Counter类是非线程安全的。大家如若第三个线程在第⑥7行代码处执行达成后,还并未履行第⑥8行代码时,第四个线程也实施了第67行代码,这一个时候counter的变量值自增了一次,然后,那多少个线程同时履行了第④8行处的代码,那会导致counter的变量只自减了1遍,由此,造成了不得法的结果。

1.7 线程优先级

Windows操作系统为抢占式八线程(Preemptive
multithreaded)操作系统,是因为线程可在此外时刻甘休(被枪占)并调度另贰个线程。

Windows操作系统中线程有0(最低) ~ 31(最高)的优先级,而优先级越高所能占用的CPU时间就更多,鲜明有个别线程所处的优先级需求考虑经过优先级对峙线程优先级八个优先级。

  1. 进度优先级:Windows协助四个经过优先级,分别是Idle、Below Normal、Normal、Above normal、High 和Realtime。默认为Normal
  2. 争论线程优先级:相对线程优先级是相对于经过优先级的,因为经过包涵了线程。Windows帮忙8个相对线程优先级,分别是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。CL途胜为和谐保留了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差别会小片段。

亚洲必赢官网 12

  为了确认保障不发出上述不科学的情事,大家无法不保证在某一个线程访问counter变量时,此外全数的线程必须等待其推行达成才能继续访问,我们能够应用lock关键字来完成这几个效率。假诺大家在有个别线程中锁定1个对象,其他兼具线程必须等到该线程解锁之后才能访问到这么些目的,因而,能够制止上述意况的产生。不过要注意的是,使用那种办法会严重影响程序的习性。更好的点子大家将会在仙童一起中描述。

  为了保证不爆发上述不正确的场合,大家务必确认保证在某一个线程访问counter变量时,此外全部的线程必须等待其执行实现才能持续走访,大家得以运用lock关键字来成功那一个效应。借使大家在某些线程中锁定2个目的,别的具备线程必须等到该线程解锁之后才能访问到这些指标,因而,可防止止上述情状的发出。不过要专注的是,使用这种方法会严重影响程序的性质。更好的方法大家将会在仙童联袂中讲述。

1.8 前台线程和后台线程

在CLTiggo中,线程要么是前台线程,要么便是后台线程。当一个经过的具备前台线程结束运营时,CL奔驰G级将强制结束仍在运营的别的后台线程,不会抛出非凡。

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

运营结果如下图所示。当前台线程十四回巡回停止之后,创设的后台线程和线程池线程都会被CL冠道强制截至。

亚洲必赢官网 13

⑩ 、使用Monitor锁定线程

⑩ 、使用Monitor锁定线程

1.9 向线程传递参数

向线程中传递参数常用的有三种办法,构造函数字传送值、Start方法传值和兰姆da表明式传值,一般常用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}");
        }
    }
}

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

亚洲必赢官网 14

   在这一小节中,大家将讲述三个三十二线程编制程序中的常见的2个难题:死锁。我们先是创立贰个死锁的演示,然后利用Monitor防止死锁的产生。

   在这一小节中,大家将讲述1个十二线程编制程序中的常见的3个题材:死锁。大家率先创设二个死锁的言传身教,然后利用Monitor防止死锁的发生。

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();
}

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

亚洲必赢官网 15

壹 、使用Visual Studio 二零一四成立二个新的控制台应用程序。

壹 、使用Visual Studio 2016创制贰个新的控制台应用程序。

1.11 使用Monitor类锁定财富

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

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()方法在逾期之后会回来,不会堵塞线程,所以并未发出死锁。而第2段代码中lock尚无过期再次来到的成效,导致能源死锁,同步块中的代码永远不会被执行。

亚洲必赢官网 16

二 、双击打开“Program.cs”文件,编写代码如下:

② 、双击打开“Program.cs”文件,编写代码如下:

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

运转结果如下图所示,与预期结果一致。

亚洲必赢官网 17

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe10
 7 {
 8     class Program
 9     {
10         static void LockTooMuch(object lock1, object lock2)
11         {
12             lock (lock1)
13             {
14                 Sleep(1000);
15                 lock (lock2)
16                 {
17                 }
18             }
19         }
20 
21         static void Main(string[] args)
22         {
23             object lock1 = new object();
24             object lock2 = new object();
25 
26             new Thread(() => LockTooMuch(lock1, lock2)).Start();
27 
28             lock (lock2)
29             {
30                 WriteLine("This will be a deadlock!");
31                 Sleep(1000);
32                 lock (lock1)
33                 {
34                     WriteLine("Acquired a protected resource succesfully");
35                 }
36             }
37         }
38     }
39 }
 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe10
 7 {
 8     class Program
 9     {
10         static void LockTooMuch(object lock1, object lock2)
11         {
12             lock (lock1)
13             {
14                 Sleep(1000);
15                 lock (lock2)
16                 {
17                 }
18             }
19         }
20 
21         static void Main(string[] args)
22         {
23             object lock1 = new object();
24             object lock2 = new object();
25 
26             new Thread(() => LockTooMuch(lock1, lock2)).Start();
27 
28             lock (lock2)
29             {
30                 WriteLine("This will be a deadlock!");
31                 Sleep(1000);
32                 lock (lock1)
33                 {
34                     WriteLine("Acquired a protected resource succesfully");
35                 }
36             }
37         }
38     }
39 }

参照书籍

本文重要参考了以下几本书,在此对那一个小编表示诚心的蒙恩被德你们提供了这样好的资料。

  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
艾德ition》那本书的结构,一共更新11个章节,先立个Flag。


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

三 、运营该控制台应用程序,运维效果如下图所示:

③ 、运维该控制台应用程序,运转效果如下图所示:

作者水平有限,假若不当欢迎各位批评指正!

亚洲必赢官网 18

亚洲必赢官网 19

  在上述结果中我们能够见到程序发生了死锁,程序一向甘休不了。

  在上述结果中我们能够看看程序爆发了死锁,程序一贯停止不了。

  在第10~19行代码处,大家定义了三个名为“LockTooMuch”的主意,在该措施中大家锁定了第多少个指标lock1,等待1分钟后,希望锁定第②个对象lock2。

  在第10~19行代码处,我们定义了2个名为“LockTooMuch”的方法,在该办法中我们锁定了第3个对象lock1,等待1分钟后,希望锁定第2个目的lock2。

  在第叁6行代码处,大家创立了一个新的线程来实施“LockTooMuch”方法,然后随即实施第三8行代码。

  在第三6行代码处,我们创立了3个新的线程来施行“LockTooMuch”方法,然后立时施行第壹8行代码。

  在第28~32行代码处,大家在主线程中锁定了目的lock2,然后等待1分钟后,希望锁定第2个对象lock1。

  在第28~32行代码处,大家在主线程中锁定了对象lock2,然后等待1分钟后,希望锁定第贰个指标lock1。

  在开创的新线程中大家锁定了对象lock1,等待1秒钟,希望锁定指标lock2,而那几个时候对象lock2已经被主线程锁定,所以新建线程会等待对象lock2被主线程解锁。然则,在主线程中,大家锁定了对象lock2,等待1分钟,希望锁定指标lock1,而以此时候对象lock1已经被创制的线程锁定,所以主线程会等待对象lock1被创设的线程解锁。当发生那种气象的时候,死锁就时有产生了,所以大家的控制台应用程序近期无法平日结束。

  在创建的新线程中大家锁定了对象lock1,等待1分钟,希望锁定指标lock2,而以此时候对象lock2已经被主线程锁定,所以新建线程会等待对象lock2被主线程解锁。但是,在主线程中,大家锁定了指标lock2,等待1分钟,希望锁定指标lock1,而以此时候对象lock1已经被制造的线程锁定,所以主线程会等待对象lock1被创建的线程解锁。当产生那种景观的时候,死锁就发生了,所以我们的控制台应用程序近年来不或许寻常甘休。

肆 、要避免死锁的发出,大家得以选用“Monitor.TryEnter”方法来替换lock关键字,“Monitor.TryEnter”方法在呼吁不到财富时不会卡住等待,能够设置超时时间,获取不到直接重临false。修改代码如下所示:

④ 、要幸免死锁的发出,大家能够使用“Monitor.TryEnter”方法来替换lock关键字,“Monitor.TryEnter”方法在呼吁不到财富时不会卡住等待,能够安装超时时间,获取不到平素回到false。修改代码如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe10
 7 {
 8     class Program
 9     {
10         static void LockTooMuch(object lock1, object lock2)
11         {
12             lock (lock1)
13             {
14                 Sleep(1000);
15                 lock (lock2)
16                 {
17                 }
18             }
19         }
20 
21         static void Main(string[] args)
22         {
23             object lock1 = new object();
24             object lock2 = new object();
25 
26             new Thread(() => LockTooMuch(lock1, lock2)).Start();
27 
28             lock (lock2)
29             {
30                 WriteLine("This will be a deadlock!");
31                 Sleep(1000);
32                 //lock (lock1)
33                 //{
34                 //    WriteLine("Acquired a protected resource succesfully");
35                 //}
36                 if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
37                 {
38                     WriteLine("Acquired a protected resource succesfully");
39                 }
40                 else
41                 {
42                     WriteLine("Timeout acquiring a resource!");
43                 }
44             }
45         }
46     }
47 }
 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe10
 7 {
 8     class Program
 9     {
10         static void LockTooMuch(object lock1, object lock2)
11         {
12             lock (lock1)
13             {
14                 Sleep(1000);
15                 lock (lock2)
16                 {
17                 }
18             }
19         }
20 
21         static void Main(string[] args)
22         {
23             object lock1 = new object();
24             object lock2 = new object();
25 
26             new Thread(() => LockTooMuch(lock1, lock2)).Start();
27 
28             lock (lock2)
29             {
30                 WriteLine("This will be a deadlock!");
31                 Sleep(1000);
32                 //lock (lock1)
33                 //{
34                 //    WriteLine("Acquired a protected resource succesfully");
35                 //}
36                 if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
37                 {
38                     WriteLine("Acquired a protected resource succesfully");
39                 }
40                 else
41                 {
42                     WriteLine("Timeout acquiring a resource!");
43                 }
44             }
45         }
46     }
47 }

五 、运维该控制台应用程序,运行效果如下图所示:

⑤ 、运转该控制台应用程序,运营效果如下图所示:

亚洲必赢官网 20

亚洲必赢官网 21

  此时,大家的控制台应用程序就制止了死锁的发生。

  此时,大家的控制台应用程序就制止了死锁的发生。

十① 、处理万分

十壹 、处理格外

   在这一小节中,大家描述怎么着在线程中国中国科学技术大学学学地拍卖非凡。正确地将try/catch块放置在线程内部是万分主要的,因为在线程外部捕获线程内部的要命日常是不容许的。

   在这一小节中,大家描述怎么着在线程中国科高校学地处理万分。正确地将try/catch块放置在线程内部是极度重要的,因为在线程外部捕获线程内部的11分平时是不容许的。

一 、使用Visual Studio 2016创建2个新的控制台应用程序。

① 、使用Visual Studio 贰零壹肆创立2个新的控制台应用程序。

二 、双击打开“Program.cs”文件,修改代码如下所示:

② 、双击打开“Program.cs”文件,修改代码如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe11
 7 {
 8     class Program
 9     {
10         static void BadFaultyThread()
11         {
12             WriteLine("Starting a faulty thread...");
13             Sleep(TimeSpan.FromSeconds(2));
14             throw new Exception("Boom!");
15         }
16 
17         static void FaultyThread()
18         {
19             try
20             {
21                 WriteLine("Starting a faulty thread...");
22                 Sleep(TimeSpan.FromSeconds(1));
23                 throw new Exception("Boom!");
24             }
25             catch(Exception ex)
26             {
27                 WriteLine($"Exception handled: {ex.Message}");
28             }
29         }
30 
31         static void Main(string[] args)
32         {
33             var t = new Thread(FaultyThread);
34             t.Start();
35             t.Join();
36 
37             try
38             {
39                 t = new Thread(BadFaultyThread);
40                 t.Start();
41             }
42             catch (Exception ex)
43             {
44                 WriteLine(ex.Message);
45                 WriteLine("We won't get here!");
46             }
47         }
48     }
49 }
 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe11
 7 {
 8     class Program
 9     {
10         static void BadFaultyThread()
11         {
12             WriteLine("Starting a faulty thread...");
13             Sleep(TimeSpan.FromSeconds(2));
14             throw new Exception("Boom!");
15         }
16 
17         static void FaultyThread()
18         {
19             try
20             {
21                 WriteLine("Starting a faulty thread...");
22                 Sleep(TimeSpan.FromSeconds(1));
23                 throw new Exception("Boom!");
24             }
25             catch(Exception ex)
26             {
27                 WriteLine($"Exception handled: {ex.Message}");
28             }
29         }
30 
31         static void Main(string[] args)
32         {
33             var t = new Thread(FaultyThread);
34             t.Start();
35             t.Join();
36 
37             try
38             {
39                 t = new Thread(BadFaultyThread);
40                 t.Start();
41             }
42             catch (Exception ex)
43             {
44                 WriteLine(ex.Message);
45                 WriteLine("We won't get here!");
46             }
47         }
48     }
49 }

三 、运行该控制台应用程序,运转效果如下图所示:

③ 、运营该控制台应用程序,运转效果如下图所示:

亚洲必赢官网 22

亚洲必赢官网 23

  在第10~15行代码处,大家定义了一个名为“BadFaultyThread”的章程,在该方法中抛出2个非凡,并且没有应用try/catch块捕获该特别。

  在第10~15行代码处,大家定义了叁个名为“BadFaultyThread”的法子,在该措施中抛出叁个格外,并且没有采纳try/catch块捕获该特别。

  在第17~29行代码处,大家定义了2个名为“FaultyThread”的措施,在该方法中也抛出2个要命,不过大家选拔了try/catch块捕获了该尤其。

  在第17~29行代码处,大家定义了一个名为“FaultyThread”的办法,在该措施中也抛出2个分外,不过大家采取了try/catch块捕获了该越发。

  在第33~35行代码处,我们创制了一个线程,在该线程中履行了“FaultyThread”方法,大家得以看出在那些新创立的线程中,大家正确地捕获了在“FaultyThread”方法中抛出的不行。

  在第33~35行代码处,大家创立了三个线程,在该线程中履行了“FaultyThread”方法,大家得以看到在这么些新创立的线程中,大家正确地捕获了在“FaultyThread”方法中抛出的不胜。

  在第37~46行代码处,我们又新创造了贰个线程,在该线程中执行了“BadFaultyThread”方法,并且在主线程中应用try/catch块来捕获在新创制的线程中抛出的可怜,不幸的的是我们在主线程中不可能捕获在新线程中抛出的万分。

  在第37~46行代码处,大家又新成立了一个线程,在该线程中实践了“BadFaultyThread”方法,并且在主线程中采用try/catch块来捕获在新创造的线程中抛出的丰盛,不幸的的是我们在主线程中不能捕获在新线程中抛出的这些。

  因而能够见见,在贰个线程中抓获另二个线程中的极度经常是不可行的。

  因此能够看出,在3个线程中抓获另1个线程中的相当平时是不可行的。

  至此,多线程(基础篇)大家就讲述到那儿,之后我们将讲述线程同步相关的学识,敬请期待!

  至此,八线程(基础篇)大家就讲述到那儿,之后咱们将讲述线程同步相关的文化,敬请期待!

  源码下载

  源码下载

网站地图xml地图