目录

多线程(基础篇3),二十四线程基础篇3

  在上一篇二十四线程(基础篇2)中,大家重点描述了鲜明线程的情事、线程优先级、前台线程和后台线程以及向线程传递参数的学识,在这一篇中大家将讲述怎么着使用C#的lock关键字锁定线程、使用Monitor锁定线程以及线程中的十分处理。

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

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

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

叁 、运营该控制台应用程序,运转作效果果(每趟运营效果恐怕分裂)如下图所示:

金沙注册送58 1

  在第45行代码处,大家创造了Counter类的二个对象,该类定义了多个简短的counter变量,该变量能够自增1和自减1。然后在第六6~68行代码处,我们创建了八个线程,并使用lambda表明式将Counter对象传递给了“TestCounter”方法,那四个线程共享同八个counter变量,并且对这几个变量实行自增和自减操作,那将造成结果的不得法。假若大家往往运维这些控制台程序,它将打字与印刷出差别的counter值,有恐怕是0,但多数动静下不是。

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

  为了保证不发生上述不得法的景况,大家亟须保险在某几个线程访问counter变量时,别的全部的线程必须等待其执行落成才能继续访问,大家能够动用lock关键字来形成那几个职能。假设大家在某些线程中锁定三个对象,其余具有线程必须等到该线程解锁之后才能访问到那一个指标,由此,可防止止上述情况的产生。可是要注意的是,使用那种方法会严重影响程序的习性。更好的章程大家将会在仙童一同中描述。

⑩ 、使用Monitor锁定线程

   在这一小节中,大家将讲述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 }

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

金沙注册送58 2

  在上述结果中大家得以看出程序爆发了死锁,程序一直甘休不了。

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

  在第一6行代码处,大家创造了三个新的线程来推行“LockTooMuch”方法,然后立刻施行第18行代码。

  在第28~32行代码处,大家在主线程中锁定了对象lock2,然后等待1分钟后,希望锁定第5个指标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 }

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

金沙注册送58 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 }

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

金沙注册送58 4

  在第10~15行代码处,大家定义了一个名为“BadFaultyThread”的点子,在该办法中抛出贰个百般,并且没有动用try/catch块捕获该越发。

  在第17~29行代码处,大家定义了一个名为“FaultyThread”的不二法门,在该格局中也抛出三个格外,可是大家使用了try/catch块捕获了该尤其。

  在第33【金沙注册送58】多线程之基础篇3,四线程基础篇3。~35行代码处,大家成立了一个线程,在该线程中实施了“FaultyThread”方法,我们能够见见在这一个新创立的线程中,大家科学地捕获了在“FaultyThread”方法中抛出的非凡。

  在第37~46行代码处,我们又新成立了贰个线程,在该线程中实践了“BadFaultyThread”方法,并且在主线程中选取try/catch块来捕获在新制造的线程中抛出的老大,不幸的的是大家在主线程中不能捕获在新线程中抛出的要命。

  由此能够见见,在3个线程中捕获另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#二十十二线程编制程序连串(二)- 线程基础


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

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

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

运转结果如下图所示,大家能够通过运行结果查出上边包车型地铁代码成立了三个线程,然后主线程和创制的线程交叉输出结果,那表明PrintNumbers方法同时运转在主线程和别的1个线程中。

金沙注册送58 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秒。验证了上面的传教,它的上床是起码让线程睡眠多久,而不是迟早多久。

金沙注册送58 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")}");
        }
    }
}

金沙注册送58 ,运维结果如下图所示,开首施行和施行完结两条消息由主线程打字与印刷;依照其出口的依次可知主线程是等待其余的线程停止后才输出执行完成那条新闻。

金沙注册送58 7

金沙注册送58 8

金沙注册送58 9

1.5 终止线程

终止线程使用的章程是Abort措施,当该方法被实施时,将尝试销毁该线程。通过吸引ThreadAbortException格外使线程被灭绝。但一般不引进应用该办法,原因有以下几点。

  1. 使用Abort方法只是尝试行销售毁该线程,但不自然能终止线程。
  2. 假使被结束的线程在实施lock内的代码,那么终止线程会招致线程不安全。
  3. 线程终止时,CL奥迪Q7会保险本人内部的数据结构不会毁掉,然而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没有继续执行便停止了;与预期的结果一律。

金沙注册送58 10

  在第④5行代码处,我们成立了Counter类的一个目的,该类定义了1个简便的counter变量,该变量可以自增1和自减1。然后在第肆6~68行代码处,我们创立了八个线程,并行使lambda表明式将Counter对象传递给了“TestCounter”方法,那多个线程共享同3个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}");
    }
}

运营结果如下图所示,与预期的结果一律。

金沙注册送58 11

  产生那种场合是因为Counter类是非线程安全的。我们只要首个线程在第肆7行代码处执行达成后,还平昔不实施第68行代码时,第一个线程也实践了第47行代码,这几个时候counter的变量值自增了三遍,然后,那五个线程同时实施了第⑥8行处的代码,那会促成counter的变量只自减了3遍,因此,造成了不得法的结果。

  产生那种情形是因为Counter类是非线程安全的。大家只要首个线程在第⑥7行代码处执行实现后,还尚未举办第伍8行代码时,第3个线程也推行了第47行代码,这一个时候counter的变量值自增了1回,然后,那三个线程同时实行了第⑥8行处的代码,这会造成counter的变量只自减了3遍,因此,造成了不科学的结果。

1.7 线程优先级

Windows操作系统为抢占式四线程(Preemptive
multithreaded)操作系统,是因为线程可在其余时刻截止(被枪占)并调度另1个线程。

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翼虎为和谐保留了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差异会小片段。

金沙注册送58 12

  为了保障不爆发上述不得法的景况,我们务必确定保证在某一个线程访问counter变量时,其它全体的线程必须等待其推行完成才能持续走访,我们能够使用lock关键字来形成那些职能。假如大家在某些线程中锁定1个对象,其余具备线程必须等到该线程解锁之后才能访问到这么些目的,因而,可防止止上述情形的爆发。可是要专注的是,使用这种方法会严重影响程序的性质。更好的方法我们将会在仙童联袂中描述。

  为了确定保证不发出上述不科学的动静,大家必须保障在某一个线程访问counter变量时,别的全部的线程必须等待其履行落成才能继承访问,大家得以选取lock关键字来实现这么些功效。借使大家在某些线程中锁定四个指标,别的具有线程必须等到该线程解锁之后才能访问到这些目的,由此,能够幸免上述情形的爆发。不过要留心的是,使用那种方式会严重影响程序的性质。更好的艺术大家将会在仙童一道中讲述。

1.8 前台线程和后台线程

在CLLX570中,线程要么是前台线程,要么便是后台线程。当1个进度的具备前台线程结束运营时,CLSportage将强制截至仍在运行的别样后台线程,不会抛出12分。

在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瑞鹰强制截止。

金沙注册送58 13

⑩ 、使用Monitor锁定线程

10、使用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}");
        }
    }
}

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

金沙注册送58 14

   在这一小节中,大家将讲述二个四线程编制程序中的常见的贰个难题:死锁。大家先是创设2个死锁的演示,然后使用Monitor防止死锁的爆发。

   在这一小节中,咱们将讲述叁个二十八线程编制程序中的常见的叁个题材:死锁。大家率先创立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();
}

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

金沙注册送58 15

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

壹 、使用Visual Studio 二〇一五制造2个新的控制台应用程序。

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()办法在逾期过后会回去,不会卡住线程,所以并未生出死锁。而第2段代码中lock并未过期重返的成效,导致财富死锁,同步块中的代码永远不会被实施。

金沙注册送58 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}");
    }
}

运作结果如下图所示,与预期结果同样。

金沙注册送58 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# 库克book Second
艾德ition》那本书的构造,一共更新十三个章节,先立个Flag。


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

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

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

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

金沙注册送58 18

金沙注册送58 19

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

  在上述结果中我们得以见到程序发生了死锁,程序一贯截止不了。

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

  在第10~19行代码处,大家定义了贰个名为“LockTooMuch”的格局,在该措施中我们锁定了第多少个目的lock1,等待1分钟后,希望锁定第②个对象lock2。

  在第16行代码处,咱们创立了1个新的线程来举行“LockTooMuch”方法,然后立时执行第壹8行代码。

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

  在第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 }

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

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

金沙注册送58 20

金沙注册送58 21

  此时,大家的控制台应用程序就幸免了死锁的产生。

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

十一 、处理至极

十一 、处理相当

   在这一小节中,大家讲述怎么样在线程中国科高校学地处理万分。正确地将try/catch块放置在线程内部是不行重庆大学的,因为在线程外部捕获线程内部的12分经常是不容许的。

   在这一小节中,我们描述怎么样在线程中国中国科学技术大学学学地拍卖非凡。正确地将try/catch块放置在线程内部是那多少个关键的,因为在线程外部捕获线程内部的可怜经常是不容许的。

壹 、使用Visual Studio 2015创办七个新的控制台应用程序。

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

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

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

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

金沙注册送58 22

金沙注册送58 23

  在第10~15行代码处,大家定义了一个名为“BadFaultyThread”的方法,在该方法中抛出三个不胜,并且没有应用try/catch块捕获该尤其。

  在第10~15行代码处,大家定义了二个名为“BadFaultyThread”的章程,在该措施中抛出2个不行,并且没有动用try/catch块捕获该尤其。

  在第17~29行代码处,我们定义了八个名为“FaultyThread”的法子,在该形式中也抛出2个拾贰分,然而大家运用了try/catch块捕获了该特别。

  在第17~29行代码处,大家定义了3个名为“FaultyThread”的措施,在该措施中也抛出叁个老大,然而大家选取了try/catch块捕获了该特别。

  在第33~35行代码处,我们成立了三个线程,在该线程中施行了“FaultyThread”方法,大家得以看看在这一个新成立的线程中,大家正确地捕获了在“FaultyThread”方法中抛出的丰裕。

  在第33~35行代码处,大家创造了3个线程,在该线程中实施了“FaultyThread”方法,大家得以见见在那一个新制造的线程中,大家正确地捕获了在“FaultyThread”方法中抛出的卓殊。

  在第37~46行代码处,大家又新创设了2个线程,在该线程中进行了“BadFaultyThread”方法,并且在主线程中应用try/catch块来捕获在新创制的线程中抛出的老大,不幸的的是大家在主线程中无法捕获在新线程中抛出的要命。

  在第37~46行代码处,大家又新创制了三个线程,在该线程中履行了“BadFaultyThread”方法,并且在主线程中选择try/catch块来捕获在新创立的线程中抛出的分外,不幸的的是我们在主线程中不恐怕捕获在新线程中抛出的百般。

  因此能够看出,在贰个线程中捕获另1个线程中的格外经常是不可行的。

  因而能够观察,在八个线程中捕获另多少个线程中的非常经常是不可行的。

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

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

  源码下载

  源码下载

相关文章

网站地图xml地图