多线程基础篇3,三十二线程之基础篇3

目录

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

  在上一篇三十二线程(基础篇2)中,大家重视讲述了规定线程的事态、线程优先级、前台线程和后台线程以及向线程传递参数的知识,在这一篇中我们将讲述怎么着使用C#的lock关键字锁定线程、使用Monitor锁定线程以及线程中的卓殊处理。

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

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

贰 、双击打开“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。然后在第四6~68行代码处,大家创造了多个线程,并行使lambda表明式将Counter对象传递给了“TestCounter”方法,那多个线程共享同多个counter变量,并且对那么些变量实行自增和自减操作,那将导致结果的不科学。假诺大家一再运维那几个控制台程序,它将打字与印刷出不一致的counter值,有恐怕是0,但大多数景色下不是。

  爆发那种景况是因为Counter类是非线程安全的。大家如若第④个线程在第六7行代码处执行完毕后,还从未实行第六8行代码时,第1个线程也实行了第四7行代码,这几个时候counter的变量值自增了叁回,然后,这五个线程同时施行了第⑤8行处的代码,那会造成counter的变量只自减了1遍,由此,造成了不正确的结果。

  为了确定保证不发生上述不正确的情况,大家不可能不保障在某1个线程访问counter变量时,别的全数的线程必须等待其实践完毕才能继承访问,大家能够运用lock关键字来成功那几个效果。如若大家在某些线程中锁定3个对象,别的兼具线程必须等到该线程解锁之后才能访问到那个目标,因而,能够幸免上述景况的发出。但是要留心的是,使用那种艺术会严重影响程序的质量。更好的方法大家将会在仙童协助举行中描述。

十 、使用Monitor锁定线程

   在这一小节中,大家将讲述八个多线程编制程序中的常见的多个标题:死锁。大家率先创造2个死锁的以身作则,然后采取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

  在上述结果中大家得以见见程序产生了死锁,程序一向结束不了。

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

  在第②6行代码处,大家创制了一个新的线程来推行“LockTooMuch”方法,然后随即实施第38行代码。

  在第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 2016开立2个新的控制台应用程序。

二 、双击打开“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行代码处,我们定义了1个名为“FaultyThread”的措施,在该措施中也抛出三个要命,可是我们应用了try/catch块捕获了该尤其。

  在第33~35行代码处,大家创设了1个线程,在该线程中施行了“FaultyThread”方法,大家得以看看在那么些新创造的线程中,我们正确地捕获了在“FaultyThread”方法中抛出的丰富。

  在第37~46行代码处,大家又新成立了二个线程,在该线程中实践了“BadFaultyThread”方法,并且在主线程中动用try/catch块来捕获在新创设的线程中抛出的卓殊,不幸的的是我们在主线程中无法捕获在新线程中抛出的尤其。

  由此能够见见,在2个线程中抓获另二个线程中的非常平日是不可行的。

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

  源码下载

在上一篇八线程(基础篇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 2014创办三个新的控制台应用程序。

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

1.1 简介

线程基础首要不外乎线程创造、挂起、等待和截止线程。关于越来越多的线程的底层实现,CPU时间片轮转等等的学识,能够参考《深入理解计算机系统》多线程基础篇3,三十二线程之基础篇3。一书中有关进度和线程的章节,本文可是多废话。

二 、双击打开“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}");
            }
        }
    }
}

运作结果如下图所示,我们得以因此运转结果获悉上面包车型大巴代码创设了三个线程,然后主线程和开创的线程交叉输出结果,那表达PrintNumbers办法同时运营在主线程和其余1个线程中。

美高梅开户网址 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. 线程终止时,CL帕杰罗会保障本人之中的数据结构不会损坏,可是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

  在第55行代码处,大家创立了Counter类的三个对象,该类定义了三个归纳的counter变量,该变量能够自增1和自减1。然后在第⑥6~68行代码处,大家成立了多个线程,并使用lambda表达式将Counter对象传递给了“TestCounter”方法,那多少个线程共享同3个counter变量,并且对那些变量进行自增和自减操作,那将造成结果的不科学。倘使大家一再运行这几个控制台程序,它将打字与印刷出不一样的counter值,有或许是0,但当先百分之五十气象下不是。

  在第45行代码处,大家创建了Counter类的一个指标,该类定义了3个归纳的counter变量,该变量能够自增1和自减1。然后在第46~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类是非线程安全的。大家只要第几个线程在第④7行代码处执行达成后,还不曾执行第肆8行代码时,第四个线程也实践了第六7行代码,那些时候counter的变量值自增了一遍,然后,那七个线程同时推行了第五8行处的代码,那会促成counter的变量只自减了二遍,因而,造成了不正确的结果。

  发生那种场馆是因为Counter类是非线程安全的。大家假设第③个线程在第④7行代码处执行完结后,还平昔不实施第五8行代码时,第二个线程也进行了第伍7行代码,那几个时候counter的变量值自增了3次,然后,那八个线程同时履行了第⑤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帮助多少个相对线程优先级,分别是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兰德CRUISER为投机保留了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关键字来成功那么些成效。借使大家在有些线程中锁定2个对象,其余具备线程必须等到该线程解锁之后才能访问到那几个目的,因而,能够幸免上述意况的发出。然则要留心的是,使用那种格局会严重影响程序的性质。更好的方法大家将会在仙童共同中描述。

  为了保险不发生上述不得法的事态,我们必须保险在某三个线程访问counter变量时,别的全部的线程必须等待其实施完毕才能再而三访问,我们得以选拔lock关键字来形成那些作用。借使大家在有些线程中锁定三个目的,其余具有线程必须等到该线程解锁之后才能访问到那几个目的,由此,能够幸免上述意况的爆发。但是要小心的是,使用那种艺术会严重影响程序的属性。更好的艺术大家将会在仙童联合中讲述。

1.8 前台线程和后台线程

在CL奥迪Q5中,线程要么是前台线程,要么正是后台线程。当2个进程的全数前台线程甘休运作时,CLRubicon将强制截止仍在运作的任何后台线程,不会抛出10分。

在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回巡回甘休之后,成立的后台线程和线程池线程都会被CLKuga强制截至。

美高梅开户网址 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

   在这一小节中,大家将讲述叁个多线程编制程序中的常见的三个题目:死锁。大家先是创设一个死锁的演示,然后采纳Monitor防止死锁的产生。

   在这一小节中,我们将讲述一个八线程编制程序中的常见的贰个难题:死锁。大家率先创建二个死锁的以身作则,然后使用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内需考虑字符串拘留的难点,若是同1个字符串常量在多少个地点出现,也许引用的会是同1个实例。
  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 2014创制1个新的控制台应用程序。

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 二十多线程中拍卖分外

在四线程中拍卖十二分应当使用前后原则,在哪些线程发生特别那么所在的代码块肯定要有相应的极度处理。不然可能会造成程序崩溃、数据丢失。

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


源码下载点击链接
以身作则源码下载

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

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

笔者水平有限,尽管不当欢迎各位批评指正!

美高梅开户网址 18

美高梅开户网址 19

  在上述结果中大家得以旁观程序发生了死锁,程序一直停止不了。

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

  在第10~19行代码处,大家定义了3个名为“LockTooMuch”的主意,在该办法中大家锁定了第四个指标lock1,等待1分钟后,希望锁定第②个指标lock2。

  在第10~19行代码处,我们定义了2个名为“LockTooMuch”的法子,在该方法中大家锁定了第二个指标lock1,等待1秒钟后,希望锁定首个对象lock2。

  在第叁6行代码处,我们创造了一个新的线程来推行“LockTooMuch”方法,然后立时施行第18行代码。

  在第③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 二零一五创建1个新的控制台应用程序。

壹 、使用Visual Studio 2014创立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行代码处,我们定义了1个名为“BadFaultyThread”的情势,在该格局中抛出3个10分,并且没有选拔try/catch块捕获该尤其。

  在第10~15行代码处,我们定义了1个名为“BadFaultyThread”的法门,在该办法中抛出三个那2个,并且没有应用try/catch块捕获该特别。

  在第17~29行代码处,大家定义了一个名为“FaultyThread”的方法,在该方式中也抛出2个越发,不过我们使用了try/catch块捕获了该越发。

  在第17~29行代码处,大家定义了二个名为“FaultyThread”的章程,在该办法中也抛出一个百般,不过我们使用了try/catch块捕获了该特别。

  在第33美高梅开户网址 ,~35行代码处,大家创立了三个线程,在该线程中施行了“FaultyThread”方法,我们能够看看在那几个新创设的线程中,大家科学地破获了在“FaultyThread”方法中抛出的分外。

  在第33~35行代码处,大家创立了一个线程,在该线程中实施了“FaultyThread”方法,大家能够见见在这些新创立的线程中,我们科学地破获了在“FaultyThread”方法中抛出的老大。

  在第37~46行代码处,大家又新创制了1个线程,在该线程中实践了“BadFaultyThread”方法,并且在主线程中运用try/catch块来捕获在新创设的线程中抛出的卓殊,不幸的的是大家在主线程中不可能捕获在新线程中抛出的十三分。

  在第37~46行代码处,我们又新成立了多少个线程,在该线程中履行了“BadFaultyThread”方法,并且在主线程中动用try/catch块来捕获在新创造的线程中抛出的要命,不幸的的是我们在主线程中不能捕获在新线程中抛出的可怜。

  因而能够看出,在八个线程中捕获另多少个线程中的分外平常是不可行的。

  因而可以见见,在三个线程中捕获另一个线程中的非常平日是不可行的。

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

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

  源码下载

  源码下载

发表评论

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

网站地图xml地图