澳门新萄京 10

四线程编程种类,明白八线程编程

目录

你必须调控的八线程编制程序,驾驭多线程编程

澳门新萄京 ,1、八线程编制程序必备知识

    1.1 进度与线程的定义

       
 当大家展开三个应用程序后,操作系统就能为该应用程序分配三个历程ID,举个例子张开QQ,你就要任务管理器的长河选项卡看到QQ.exe进程,如下图:

         澳门新萄京 1

         
进度能够清楚为一块包罗了有个别能源的内部存款和储蓄器区域,操作系统通过进程这一方式把它的干活划分为区别的单元。多个应用程序能够对应于多少个进程。

         
线程是经过中的独立试行单元,对于操作系统来说,它通过调整线程来使应用程序专门的学业,三个进程中至少含有一个线程,大家把该线程成为主线程。线程与经过之间的涉嫌足以清楚为:线程是经过的试行单元,操作系统通过调治线程来使应用程序职业;而经过则是线程的容器,它由操作系统创设,又在切实的举办进度中开创了线程。

 

    1.2 线程的调节

       
 在操作系统的书中一般有提过,“Windows是抢占式二十三十六线程操作系统”。之所以那样说它是抢占式的,是因为线程能够在放肆时间里被占领,来调解另八个线程。操作系统为各种线程分配了0-3第11中学的某超级优先级,而且会把初期级高的线程优分给CPU实行。

         
Windows帮忙7个相对线程优先级:Idle、Lowest、BelowNormal、Normal、AboveNormal、Highest和Time-Critical。在那之中,Normal是暗许的线程优先级。程序可以经过设置Thread的Priority属性来改造线程的优先级,该属性的品种为ThreadPriority枚举类型,其成员包涵Lowest、BelowNormal、诺玛l、AboveNormal和Highest。CLPRADO为自个儿童卫生保健留了Idle和Time-Critical四个优先级。

 

    1.3 线程也分前后台

         
线程有前台线程和后台线程之分。在一个历程中,当有着前台线程甘休运维后,CL福睿斯会强制停止全部仍在运作的后台线程,那几个后台线程被直接终止,却不会抛出其余格外。主线程将从来是前台线程。大家得以运用Tread类来创立前台线程。

 1 using System;
 2 using System.Threading;
 3 
 4 namespace 多线程1
 5 {
 6     internal class Program
 7     {
 8         private static void Main(string[] args)
 9         {
10             var backThread = new Thread(Worker);
11             backThread.IsBackground = true;
12             backThread.Start();
13             Console.WriteLine("从主线程退出");
14             Console.ReadKey();
15         }
16 
17         private static void Worker()
18         {
19             Thread.Sleep(1000);
20             Console.WriteLine("从后台线程退出");
21         }
22     }
23 }

   
以上代码先通过Thread类创制了八个线程对象,然后经过设置IsBackground属性来指明该线程为后台线程。即使不安装这些天性,则默以为前台线程。接着调用了Start的点子,此时后台线程会实行Worker函数的代码。所以在这些程序中有七个线程,三个是运作Main函数的主线程,贰个是运营Worker线程的后台线程。由于前台线程试行完结后CL昂Cora会无条件地休息后台线程的运作,所以在头里的代码中,若运维了后台线程,则主线程将会持续运转。主线程施行完后,CL途观开掘主线程停止,会终止后台线程,然后使任何应用程序截止运维,所以Worker函数中的Console语句将不会试行。所以地点代码的结果是不会运作Worker函数中的Console语句的。

   
 能够利用Join函数的不二等秘书籍,确定保障主线程会在后台线程实行完结后才早先运维。

 1 using System;
 2 using System.Threading;
 3 
 4 namespace 多线程1
 5 {
 6     internal class Program
 7     {
 8         private static void Main(string[] args)
 9         {
10             var backThread = new Thread(Worker);
11             backThread.IsBackground = true;
12             backThread.Start();
13             backThread.Join();
14             Console.WriteLine("从主线程退出");
15             Console.ReadKey();
16         }
17 
18         private static void Worker()
19         {
20             Thread.Sleep(1000);
21             Console.WriteLine("从后台线程退出");
22         }
23     }
24 }

    以上代码调用Join函数来确认保证主线程会在后台线程停止后再运维。

    借使你线程施行的议程供给参数,则就要求采纳new
Thread的重载构造函数Thread(ParameterizedThreadStart).

 1 using System;
 2 using System.Threading;
 3 
 4 namespace 多线程1
 5 {
 6     internal class Program
 7     {
 8         private static void Main(string[] args)
 9         {
10             var backThread = new Thread(new ParameterizedThreadStart(Worker));
11             backThread.IsBackground = true;
12             backThread.Start("Helius");
13             backThread.Join();
14             Console.WriteLine("从主线程退出");
15             Console.ReadKey();
16         }
17 
18         private static void Worker(object data)
19         {
20             Thread.Sleep(1000);
21             Console.WriteLine($"传入的参数为{data.ToString()}");
22         }
23     }
24 }

   
试行结果为:澳门新萄京 2

 

2、线程的器皿——线程池

   
前边我们都以通过Thead类来手动创设线程的,可是线程的创制和销毁会消耗大批量时刻,那样的手动操作将促成品质损失。由此,为了防止因经过Thread手动创制线程而导致的损失,.NET引进了线程池机制。

    2.1 线程池

       
 线程池是指用来存放在应用程序中要选取的线程会集,能够将它了然为二个存放线程的地点,这种汇聚存放的诀窍方便对线程举办保管。

       
 CL牧马人开首化时,线程池中是未曾线程的。在内部,线程池维护了叁个操作央求队列,当应用程序想要实行一个异步操作时,供给调用QueueUserWorkItem方法来将相应的天职加多到线程池的央求队列中。线程池达成的代码会从队列中领取,并将其委派给线程池中的线程去施行。假使线程池未有空闲的线程,则线程池也会制造三个新线程去实践提取的职务。而当线程池线程实现有个别职责时,线程不会被销毁,而是回到到线程池中,等待响应另二个伸手。由于线程不会被灭绝,所以也就防止了品质损失。记住,线程池里的线程都以往台线程,暗中认可品级是诺玛l。

 

    2.2 通过线程池来促成八线程

         
要使用线程池的线程,须求调用静态方法ThreadPool.QueueUserWorkItem,以内定线程要调用的点子,该静态方法有八个重载版本:

          public static bool QueueUserWorkItem(WaitCallback callBack);

          public static bool QueueUserWorkItem(WaitCallback
callback,Object state)

         
那五个法子用于向线程池队列加多二个干活先以及一个可选的图景数据。然后,那多少个点子就能够及时赶回。上边通超过实际例来演示怎么着使用线程池来贯彻二十四线程编制程序。

 1 using System;
 2 using System.Threading;
 3 
 4 namespace 多线程2
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             Console.WriteLine($"主线程ID={Thread.CurrentThread.ManagedThreadId}");
11             ThreadPool.QueueUserWorkItem(CallBackWorkItem);
12             ThreadPool.QueueUserWorkItem(CallBackWorkItem,"work");
13             Thread.Sleep(3000);
14             Console.WriteLine("主线程退出");
15             Console.ReadKey();
16         }
17 
18         private static void CallBackWorkItem(object state)
19         {
20             Console.WriteLine("线程池线程开始执行");
21             if (state != null)
22             {
23                 Console.WriteLine($"线程池线程ID={Thread.CurrentThread.ManagedThreadId},传入的参数为{state.ToString()}");
24             }
25             else
26             {
27                 Console.WriteLine($"线程池线程ID={Thread.CurrentThread.ManagedThreadId}");
28             }
29         }
30     }
31 }

结果为:澳门新萄京 3

 

    2.3 合营式打消线程池线程

         .NET
Framework提供了撤除操作的方式,那一个形式是合营式的。为了撤销三个操作,必须创建三个System.Threading.CancellationTokenSource对象。下边依然使用代码来演示一下:

using System;
using System.Threading;

namespace 多线程3
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine("主线程运行");
            var cts = new CancellationTokenSource();
            ThreadPool.QueueUserWorkItem(Callback, cts.Token);
            Console.WriteLine("按下回车键来取消操作");
            Console.Read();
            cts.Cancel();
            Console.ReadKey();
        }

        private static void Callback(object state)
        {
            var token = (CancellationToken) state;
            Console.WriteLine("开始计数");
            Count(token, 1000);
        }

        private static void Count(CancellationToken token, int count)
        {
            for (var i = 0; i < count; i++)
            {
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("计数取消");
                    return;
                }
                Console.WriteLine($"计数为:{i}");
                Thread.Sleep(300);
            }
            Console.WriteLine("计数完成");
        }
    }
}

结果为:澳门新萄京 4

 

3、线程同步

   
线程同步计数是指多线程程序中,为了保障后者线程,惟有拭目以俟前者线程落成未来才具继续实行。那就好比活着中排队买票,在如今的人没买到票从前,前面包车型大巴人总得等待。

    3.1 多线程程序中留存的隐患

         
多线程只怕同不日常候去拜谒多个共享能源,那将损坏财富中所保存的数目。这种状态下,只可以利用线程同步技艺。

    3.2 使用监视器对象实现线程同步

         
监视器对象(Monitor)能够保证线程具有对共享资源的排外访问权,C#经过lock关键字来提供简化的语法。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 namespace 线程同步
 9 {
10     class Program
11     {
12         private static int tickets = 100;
13         static object globalObj=new object();
14         static void Main(string[] args)
15         {
16             Thread thread1=new Thread(SaleTicketThread1);
17             Thread thread2=new Thread(SaleTicketThread2);
18             thread1.Start();
19             thread2.Start();
20             Console.ReadKey();
21         }
22 
23         private static void SaleTicketThread2()
24         {
25             while (true)
26             {
27                 try
28                 {
29                     Monitor.Enter(globalObj);
30                     Thread.Sleep(1);
31                     if (tickets > 0)
32                     {
33                         Console.WriteLine($"线程2出票:{tickets--}");
34                     }
35                     else
36                     {
37                         break;
38                     }
39                 }
40                 catch (Exception)
41                 {
42                     throw;
43                 }
44                 finally
45                 {
46                     Monitor.Exit(globalObj);
47                 }
48             }
49         }
50 
51         private static void SaleTicketThread1()
52         {
53             while (true)
54             {
55                 try
56                 {
57                     Monitor.Enter(globalObj);
58                     Thread.Sleep(1);
59                     if (tickets > 0)
60                     {
61                         Console.WriteLine($"线程1出票:{tickets--}");
62                     }
63                     else
64                     {
65                         break;
66                     }
67                 }
68                 catch (Exception)
69                 {
70                     throw;
71                 }
72                 finally
73                 {
74                     Monitor.Exit(globalObj);
75                 }
76             }
77         }
78     }
79 }

   
在以上代码中,首先额外定义了叁个静态全局变量globalObj,并将其看做参数字传送递给Enter方法。使用了Monitor锁定的目的须要为引用类型,而不可能为值类型。因为在将值类型传递给Enter时,它将被先装箱为多少个单独的毒香,之后再传递给Enter方法;而在将变量传递给Exit方法时,也会创设贰个单身的引用对象。此时,传递给Enter方法的对象和传递给Exit方法的指标分裂,Monitor将会吸引SynchronizationLockException至极。

  

    3.3 线程同步本事存在的主题材料

       
 (1)使用比较繁琐。要用额外的代码把四个线程同不常候做客的数码包围起来,还并不可能遗漏。

       
 (2)使用线程同步会影响程序质量。因为获取和刑释同步锁是急需时日的;并且决定非常线程先拿走锁的时候,CPU也要举行和谐。这几个额外的做事都会对质量形成影响。

       
 (3)线程同步每一遍只允许三个线程访问财富,那会产生线程堵塞。继而系统会创造越来越多的线程,CPU也就要负责更辛劳的调节工作。那个进度会对质量形成影响。

           下边就由代码来解释一下品质的出入:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Diagnostics;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading;
 7 using System.Threading.Tasks;
 8 
 9 namespace 线程同步2
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             int x = 0;
16             const int iterationNumber = 5000000;
17             Stopwatch stopwatch=Stopwatch.StartNew();
18             for (int i = 0; i < iterationNumber; i++)
19             {
20                 x++;
21             }
22             Console.WriteLine($"不使用锁的情况下花费的时间:{stopwatch.ElapsedMilliseconds}ms");
23             stopwatch.Restart();
24             for (int i = 0; i < iterationNumber; i++)
25             {
26                 Interlocked.Increment(ref x);
27             }
28             Console.WriteLine($"使用锁的情况下花费的时间:{stopwatch.ElapsedMilliseconds}ms");
29             Console.ReadKey();
30         }
31     }
32 }

   
实施结果:澳门新萄京 5

    实施出结论。

1、多线程编制程序必备知识 1.1 进度与线程的概念
当大家开荒二个应用程序后,操作系统就能为该…

  • C#多线程编制程序连串(二)-
    线程基础

    • 1.1
      简介
    • 1.2
      创造线程
    • 1.3
      暂停线程
    • 1.4
      线程等待
    • 1.5
      终止线程
    • 1.6
      检查实验线程状态
    • 1.7
      线程优先级
    • 1.8
      前台线程和后台线程
    • 1.9
      向线程传递参数
    • 1.10 C#
      Lock关键字的使用
    • 1.11
      使用Monitor类锁定财富
    • 1.12
      八线程中拍卖特别
  • 参照书籍
  • 作者水平有限,假使不当接待各位批评指正!

C#八线程编制程序类别(二)- 线程基础


1.1 简介

线程基础主要归纳线程成立、挂起、等待和终止线程。关于越来越多的线程的底部完成,CPU时间片轮转等等的学问,能够参见《深入理解计算机系统》一书中关于进程和线程的章节,本文然而多废话。

1.2 创立线程

在C#言语中,创立线程是一件非常轻巧的工作;它只供给用到
System.Threading命名空间,当中第一运用Thread类来成立线程。

示范代码如下所示:

using System;
using System.Threading; // 创建线程需要用到的命名空间
namespace Recipe1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1.创建一个线程 PrintNumbers为该线程所需要执行的方法
            Thread t = new Thread(PrintNumbers);
            // 2.启动线程
            t.Start();

            // 主线程也运行PrintNumbers方法,方便对照
            PrintNumbers();
            // 暂停一下
            Console.ReadKey();
        }

        static void PrintNumbers()
        {
            // 使用Thread.CurrentThread.ManagedThreadId 可以获取当前运行线程的唯一标识,通过它来区别线程
            Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 开始打印...");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId} 打印:{i}");
            }
        }
    }
}

运营结果如下图所示,大家能够经过运转结果查出上边的代码创制了贰个线程,然后主线程和创办的线程交叉输出结果,那表明PrintNumbers措施同临时间运营在主线程和别的八个线程中。

澳门新萄京 6

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秒。验证了地点的传教,它的小憩是起码让线程睡眠多久,而不是必然多久。

澳门新萄京 7

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

运作结果如下图所示,开端举行和实行完结两条消息由主线程打印;依照其出口的顺序可知主线程是等待其它的线程截止后才输出推行达成那条音讯。

澳门新萄京 8

1.5 终止线程

悬停线程使用的法子是Abort方法,当该措施被试行时,将尝试销毁该线程。通过吸引ThreadAbortException十三分使线程被销毁。但一般不引入使用该方法,原因有以下几点。

  1. 使用Abort措施只是尝尝销毁该线程,但不必然能终止线程。
  2. 假使被结束的线程在进行lock内的代码,那么终止线程会产生线程不安全。
  3. 线程终止时,CLKoleos会保障自身之中的数据结构不会损坏,不过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未有继续试行便停止了;与预期的结果一律。

澳门新萄京 9

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

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

澳门新萄京 10

发表评论

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