威尼斯人线上娱乐

【威尼斯人线上娱乐】四线程基础篇3,线程基础

31 3月 , 2019  

目录

八线程(基础篇3),多线程基础篇3

  在上一篇多线程(基础篇2)中,大家重点描述了规定线程的动静、线程优先级、前台线程和后台线程以及向线程传递参数的文化,在这一篇中大家将讲述如何使用C#的lock关键字锁定线程、使用Monitor锁定线程以及线程中的非常处理。

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

一 、使用Visual Studio 二〇一四开立2个新的控制台应用程序。

二 、双击打开“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。然后在第56~68行代码处,大家制造了四个线程,并运用lambda表达式将Counter对象传递给了“TestCounter”方法,那四个线程共享同一个counter变量,并且对那几个变量举办自增和自减操作,那将造成结果的不科学。若是大家一再运行那几个控制台程序,它将打字与印刷出差异的counter值,有可能是0,但大部分景观下不是。

  发生这种情形是因为Counter类是非线程安全的。大家只要首个线程在第四7行代码处执行完结后,还不曾执行第⑤8行代码时,第一个线程也实践了第陆7行代码,这么些时候counter的变量值自增了二次,然后,那七个线程同时推行了第68行处的代码,那会促成counter的变量只自减了3次,由此,造成了不正确的结果。

  为了保险不发出上述不科学的气象,大家亟须保障在某贰个线程访问counter变量时,此外全数的线程必须等待其推行达成才能持续访问,我们能够利用lock关键字来完结那么些效用。尽管大家在有个别线程中锁定叁个对象,其他具有线程必须等到该线程解锁之后才能访问到那个目的,因而,能够幸免上述处境的产生。可是要专注的是,使用那种形式会严重影响程序的性质。更好的法门大家将会在仙童联合中描述。

10、使用Monitor锁定线程

   在这一小节中,我们将讲述2个二十四线程编制程序中的常见的多少个难题:死锁。我们率先创立1个死锁的言传身教,然后使用Monitor防止死锁的发生。

一 、使用Visual Studio 二零一五开立1个新的控制台应用程序。

贰 、双击打开“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 }

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

威尼斯人线上娱乐 2

  在上述结果中大家得以见到程序产生了死锁,程序一直截止不了。

【威尼斯人线上娱乐】四线程基础篇3,线程基础。  在第10~19行代码处,我们定义了三个名为“LockTooMuch”的格局,在该办法中大家锁定了第一个指标lock1,等待1分钟后,希望锁定第③个目的lock2。

  在第③6行代码处,大家成立了1个新的线程来进行“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 二〇一六创立贰个新的控制台应用程序。

② 、双击打开“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行代码处,我们定义了三个名为“FaultyThread”的措施,在该措施中也抛出三个尤其,不过我们选择了try/catch块捕获了该尤其。

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

  在第37~46行代码处,大家又新创立了二个线程,在该线程中施行了“BadFaultyThread”方法,并且在主线程中央银行使try/catch块来捕获在新创制的线程中抛出的可怜,不幸的的是我们在主线程中无法捕获在新线程中抛出的万分。

  由此能够看来,在五个线程中捕获另3个线程中的至极平常是不可行的。

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

  源码下载

在上一篇二十八线程(基础篇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
      八线程中拍卖12分
  • 参照书籍
  • 小编水平有限,借使不当欢迎各位批评指正!

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

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

C#八线程编制程序体系(二)- 线程基础


① 、使用Visual Studio 2016创办三个新的控制台应用程序。

① 、使用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艺术同时运行在主线程和其余3个线程中。

威尼斯人线上娱乐 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方法,该情势将暂停实施当前线程,直到所等待的另2个线程终止。在简短的线程同步中会使用到,但它相比容易,不作过多介绍。

示范代码如下所示:

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办法,当该形式被实施时,将尝试销毁该线程。通过抓住ThreadAbortException11分使线程被灭绝。但貌似不推荐使用该办法,原因有以下几点。

  1. 威尼斯人线上娱乐,使用Abort措施只是尝尝销毁该线程,但不自然能终止线程。
  2. 要是被终止的线程在推行lock内的代码,那么终止线程会导致线程不安全。
  3. 线程终止时,CL陆风X8会保障自个儿之中的数据结构不会损坏,可是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类的1个对象,该类定义了1个大概的counter变量,该变量能够自增1和自减1。然后在第⑥6~68行代码处,我们创设了三个线程,并使用lambda表明式将Counter对象传递给了“TestCounter”方法,那五个线程共享同三个counter变量,并且对那么些变量举行自增和自减操作,那将造成结果的不得法。假若大家往往运行这些控制台程序,它将打字与印刷出不一致的counter值,有恐怕是0,但多数景况下不是。

  在第⑥5行代码处,大家创设了Counter类的一个指标,该类定义了二个粗略的counter变量,该变量能够自增1和自减1。然后在第⑤6~68行代码处,大家创造了多个线程,并利用lambda表明式将Counter对象传递给了“TestCounter”方法,那四个线程共享同1个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行代码处执行达成后,还没有履行第48行代码时,第三个线程也推行了第肆7行代码,这几个时候counter的变量值自增了3次,然后,这八个线程同时推行了第⑤8行处的代码,那会导致counter的变量只自减了2回,由此,造成了不正确的结果。

  产生那种情景是因为Counter类是非线程安全的。大家只要第3个线程在第六7行代码处执行完结后,还尚无执行第六8行代码时,首个线程也实践了第六7行代码,那么些时候counter的变量值自增了一次,然后,那多个线程同时履行了第五8行处的代码,那会招致counter的变量只自减了二回,因而,造成了不得法的结果。

1.7 线程优先级

Windows操作系统为抢占式多线程(Preemptive
multithreaded)操作系统,是因为线程可在其他时刻结束(被枪占)并调度另贰个线程。

Windows操作系统中线程有0(最低) ~ 31(最高)的优先级,而优先级越高所能占用的CPU时间就越来越多,鲜明有个别线程所处的预先级要求考虑进度优先级相对线程优先级三个先行级。

  1. 经过优先级:Windows协理伍个进程优先级,分别是Idle、Below Normal、Normal、Above normal、High 和Realtime。默认为Normal
  2. 对峙线程优先级:相对线程优先级是对峙于经过优先级的,因为经过包蕴了线程。Windows帮衬多少个相对线程优先级,分别是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。CLTucson为友好保留了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关键字来形成这么些功用。如若大家在有些线程中锁定贰个目的,其余具有线程必须等到该线程解锁之后才能访问到那几个指标,由此,能够制止上述情状的发生。但是要小心的是,使用那种艺术会严重影响程序的属性。更好的不二法门大家将会在仙童共同中讲述。

  为了确定保证不发出上述不科学的意况,我们亟须确定保障在某叁个线程访问counter变量时,其余全体的线程必须等待其实施落成才能接二连三走访,我们得以行使lock关键字来完结那一个效果。假设大家在有个别线程中锁定3个指标,其余全体线程必须等到该线程解锁之后才能访问到那几个目的,由此,能够幸免上述景况的发生。但是要留意的是,使用这种措施会严重影响程序的性子。更好的章程我们将会在仙童联合中描述。

1.8 前台线程和后台线程

在CLOdyssey中,线程要么是前台线程,要么便是后台线程。当二个进度的有所前台线程结束运维时,CL牧马人将强制甘休仍在运维的别的后台线程,不会抛出尤其。

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

运转结果如下图所示。当前台线程13遍巡回结束之后,成立的后台线程和线程池线程都会被CL奥迪Q5强制结束。

威尼斯人线上娱乐 13

10、使用Monitor锁定线程

⑩ 、使用Monitor锁定线程

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

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

威尼斯人线上娱乐 14

   在这一小节中,大家将讲述2个二十多线程编制程序中的常见的二个难点:死锁。大家第贰创建二个死锁的言传身教,然后利用Monitor幸免死锁的产生。

   在这一小节中,大家将讲述二个十六线程编程中的常见的一个题材:死锁。我们首先创制叁个死锁的以身作则,然后利用Monitor避免死锁的发生。

1.10 C# Lock关键字的施用

在多线程的体系中,由于CPU的流年片轮转等线程调度算法的使用,简单出现线程安全难题。具体可参看《深入理解计算机系统》一书相关的章节。

在C#中lock重中之重字是1个语法糖,它将Monitor装进,给object加上3个互斥锁,从而达成代码的线程安全,Monitor会在下一节中牵线。

对于lock根本字依然Monitor锁定的对象,都必须小心选取,不对劲的精选可能会招致惨重的属性难题依旧发出死锁。以下有几条关于选取锁定目的的提出。

  1. 一起锁定的指标无法是值类型。因为运用值类型时会有装箱的难题,装箱后的就成了三个新的实例,会招致Monitor.Enter()Monitor.Exit()收起到分化的实例而错过关联性
  2. 幸免锁定this、typeof(type)和stringthistypeof(type)锁定可能在其他不相干的代码中会有相同的定义,导致四个一块块相互阻塞。string急需考虑字符串拘禁的难题,借使同一个字符串常量在两个地点出现,可能引用的会是同3个实例。
  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

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

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

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从未有过过期再次来到的意义,导致财富死锁,同步块中的代码永远不会被实践。

威尼斯人线上娱乐 16

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

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

1.12 多线程中处理10分

在四线程中拍卖极度应当选拔前后原则,在哪些线程发生极度那么所在的代码块肯定要有照应的不行处理。不然或许会招致程序崩溃、数据丢失。

主线程中使用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》那本书的构造,一共更新十1个章节,先立个Flag。


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

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

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

小编水平有限,假使不当欢迎各位批评指正!

威尼斯人线上娱乐 18

威尼斯人线上娱乐 19

  在上述结果中大家得以看来程序发生了死锁,程序平素甘休不了。

  在上述结果中大家得以见到程序发生了死锁,程序一贯甘休不了。

  在第10~19行代码处,大家定义了一个名为“LockTooMuch”的办法,在该格局中咱们锁定了第③个对象lock1,等待1分钟后,希望锁定第②个目的lock2。

  在第10~19行代码处,咱们定义了一个名为“LockTooMuch”的不二法门,在该措施中大家锁定了第一个目的lock1,等待1分钟后,希望锁定第一个对象lock2。

  在第二6行代码处,大家创立了1个新的线程来施行“LockTooMuch”方法,然后登时施行第贰8行代码。

  在第三6行代码处,大家创造了二个新的线程来实施“LockTooMuch”方法,然后立时施行第②8行代码。

  在第28~32行代码处,大家在主线程中锁定了目的lock2,然后等待1分钟后,希望锁定第一个指标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块放置在线程内部是不行首要的,因为在线程外部捕获线程内部的特别常常是不容许的。

一 、使用Visual Studio 二〇一五创建贰个新的控制台应用程序。

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

贰 、双击打开“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行代码处,大家定义了2个名为“BadFaultyThread”的措施,在该办法中抛出1个百般,并且没有利用try/catch块捕获该特别。

  在第10~15行代码处,大家定义了一个名为“BadFaultyThread”的办法,在该格局中抛出3个可怜,并且没有采纳try/catch块捕获该尤其。

  在第17~29行代码处,大家定义了3个名为“FaultyThread”的艺术,在该办法中也抛出1个10分,可是大家选用了try/catch块捕获了该尤其。

  在第17~29行代码处,咱们定义了三个名为“FaultyThread”的点子,在该方式中也抛出三个那三个,不过大家使用了try/catch块捕获了该特别。

  在第33~35行代码处,我们创设了二个线程,在该线程中进行了“FaultyThread”方法,大家能够见到在那个新成立的线程中,大家科学地破获了在“FaultyThread”方法中抛出的要命。

  在第33~35行代码处,我们创立了2个线程,在该线程中执行了“FaultyThread”方法,大家能够看来在这几个新创制的线程中,大家科学地破获了在“FaultyThread”方法中抛出的百般。

  在第37~46行代码处,大家又新创立了三个线程,在该线程中实施了“BadFaultyThread”方法,并且在主线程中央银行使try/catch块来捕获在新创立的线程中抛出的不得了,不幸的的是我们在主线程中无法捕获在新线程中抛出的至极。

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

  因而能够观望,在叁个线程中抓获另二个线程中的相当平常是不可行的。

  由此能够看来,在2个线程中抓获另七个线程中的万分平时是不可行的。

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

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

  源码下载

  源码下载


相关文章

发表评论

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

网站地图xml地图