• C#多线程之基础篇2


      在上一篇C#多线程之基础篇1中,我们主要讲述了如何创建线程、中止线程、线程等待以及终止线程的相关知识,在本篇中我们继续讲述有关线程的一些知识。

    五、确定线程的状态

       在这一节中,我们将讲述如何查看一个线程的状态,通常知道一个线程处于什么状态是非常有用的。但是,要注意线程是独立运行的,它的状态可能随时变化。要查看一个线程的状态,我们可以按如下步骤进行:

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

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

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Recipe05
     7 {
     8     class Program
     9     {
    10         static void DoNothing()
    11         {
    12             Sleep(TimeSpan.FromSeconds(2));
    13         }
    14 
    15         static void PrintNumbersWithStatus()
    16         {
    17             WriteLine("Starting...");
    18             WriteLine(CurrentThread.ThreadState.ToString());
    19 
    20             for(int i = 1; i < 10; i++)
    21             {
    22                 Sleep(TimeSpan.FromSeconds(2));
    23                 WriteLine(i);
    24             }
    25         }
    26 
    27         static void Main(string[] args)
    28         {
    29             WriteLine("Starting program...");
    30 
    31             Thread t1 = new Thread(PrintNumbersWithStatus);
    32             Thread t2 = new Thread(DoNothing);
    33 
    34             WriteLine(t1.ThreadState.ToString());
    35 
    36             t2.Start();
    37             t1.Start();
    38 
    39             for(int i = 1; i < 30; i++)
    40             {
    41                 WriteLine(i.ToString() + " - " + t1.ThreadState.ToString());
    42             }
    43 
    44             Sleep(TimeSpan.FromSeconds(6));
    45 
    46             t1.Abort();
    47 
    48             WriteLine("A thread has been aborted");
    49             WriteLine(t1.ThreadState.ToString());
    50             WriteLine(t2.ThreadState.ToString());
    51         }
    52     }
    53 }

    3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

      在上述代码中,我们在“Main”方法中定义了两个不同的线程t1和t2,t1线程在执行过程中被终止,t2线程成功执行完毕。我们可以使用Thread对象的ThreadState属性获得某个线程的状态信息,ThreadState属性是C#枚举类型。

      当程序执行到第34行代码处时,t1线程还没有执行,这个时候t1线程的状态为“ThreadState.Unstarted”。

      当程序执行到第37行代码处时,t1线程开始执行,这个时候该线程的状态为“ThreadState.Running”。

      当运行到第39~42行代码处时,主线程开始执行循环语句,持续打印出29条t1线程的状态,因为在“PrintNumbersWithStatus”方法中调用了“Thread.Sleep”方法,因此在执行主线程的循环的过程中,t1线程的状态在“ThreadState.Running”和“ThreadState.WaitSleepJoin”之间转换。

      当程序执行到第44行代码处时,在主线程中调用了“Thread.Sleep”方法,在等待6秒期间,“PrintNumbersWithStatus”方法中的for循环代码不断执行,因此打印出1、2、3共三行数字。

      当程序执行到第46行代码处时,我们调用了t1的“Abort”方法,从而t1线程被终止。

      当程序执行到第49行代码处时,由于已经在t1线程上调用了“Abort”方法,因此,它的状态为“ThreadState.Aborted”或“ThreadState.AbortRequested”。

      当程序执行到第50行代码处时,因为t2线程正常执行完毕,因此t2线程的状态为“ThreadState.Stopped”。

    六、线程优先级

       在这一小节中,我们将讲述线程的优先级,线程的优先级确定了线程所能使用CPU时间的大小。我们按以下步骤来完成线程优先级的学习:

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

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

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 using static System.Diagnostics.Process;
     6 
     7 namespace Recipe06
     8 {
     9     class ThreadSample
    10     {
    11         private bool isStopped = false;
    12 
    13         public void Stop()
    14         {
    15             isStopped = true;
    16         }
    17 
    18         public void CountNumbers()
    19         {
    20             long counter = 0;
    21             while (!isStopped)
    22             {
    23                 counter++;
    24             }
    25 
    26             WriteLine($"{CurrentThread.Name} with {CurrentThread.Priority,10} priority has a count = {counter,15:N0}");
    27         }
    28     }
    29 
    30     class Program
    31     {
    32         static void RunThreads()
    33         {
    34             var sample = new ThreadSample();
    35 
    36             var threadOne = new Thread(sample.CountNumbers);
    37             threadOne.Name = "ThreadOne";
    38 
    39             var threadTwo = new Thread(sample.CountNumbers);
    40             threadTwo.Name = "ThreadTwo";
    41 
    42             threadOne.Priority = ThreadPriority.Highest;
    43             threadTwo.Priority = ThreadPriority.Lowest;
    44 
    45             threadOne.Start();
    46             threadTwo.Start();
    47 
    48             Sleep(TimeSpan.FromSeconds(2));
    49             sample.Stop();
    50         }
    51 
    52         static void Main(string[] args)
    53         {
    54             WriteLine($"Current thread priority: {CurrentThread.Priority}");
    55             WriteLine("Running on all cores available");
    56 
    57             RunThreads();
    58 
    59             Sleep(TimeSpan.FromSeconds(2));
    60 
    61             WriteLine("Running on a single core");
    62             GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
    63             RunThreads();
    64         }
    65     }
    66 }

    3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

      在上述代码中,我们定义了两个不同的线程threadOne和threadTwo。threadOne线程具有最高的优先级“ThreadPriority.Highest”,threadTwo具有最低的优先级“ThreadPriority.Lowest”。

      在程序执行到第57行代码处,如果我们的计算机是多核计算机,我们将在2秒内获得初始结果,并且具有最高优先级的threadOne线程要比具有最低优先级的threadTwo线程的迭代次数更多一些,但也不会相差太远。但是,如果我们的计算机是单核计算机,则结果大不相同。

      为了模拟单核计算机,我们将ProcessorAffinity的值设置为1,现在这两个线程所迭代的次数将差异非常大,并且程序的执行时间远远大于2秒。这是因为CPU的计算机时间大部分给予了高优先级的线程,而低优先级的线程获得非常少的CPU时间。

    七、前台线程和后台线程

      在这一小节中,我们将讲述什么是前台线程和后台线程,并且讲述如何设置这个选项以影响程序的行为。我们按以下步骤来完成前台线程和后台线程的学习:

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

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

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Recipe07
     7 {
     8     class ThreadSample
     9     {
    10         private readonly int iterations;
    11 
    12         public ThreadSample(int iterations)
    13         {
    14             this.iterations = iterations;
    15         }
    16 
    17         public void CountNumbers()
    18         {
    19             for(int i = 0; i < iterations; i++)
    20             {
    21                 Sleep(TimeSpan.FromSeconds(0.5));
    22                 WriteLine($"{CurrentThread.Name} prints {i}");
    23             }
    24         }
    25     }
    26 
    27     class Program
    28     {
    29         static void Main(string[] args)
    30         {
    31             var sampleForeground = new ThreadSample(10);
    32             var sampleBackground = new ThreadSample(20);
    33 
    34             var threadOne = new Thread(sampleForeground.CountNumbers);
    35             threadOne.Name = "ForegroundThread";
    36 
    37             var threadTwo = new Thread(sampleBackground.CountNumbers);
    38             threadTwo.Name = "BackgroundThread";
    39             threadTwo.IsBackground = true;
    40 
    41             threadOne.Start();
    42             threadTwo.Start();
    43         }
    44     }
    45 }

    3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

      在上述代码中,我们定义了两个不同的线程threadOne和threadTwo。当我们创建一个线程时,该线程显示地是一个前台线程,比如threadOne。如果我们要将一个线程设置为后台线程,只需要将该线程的“IsBackground”属性设置为“true”即可,比如threadTwo。我们还配置了两个线程执行的循环次数不一样,threadOne线程所执行的循环次数为10次,threadTwo线程所执行的循环次数为20次,因此threadOne线程要比threadTwo线程早执行完毕。

      从执行结果上来看,当threadOne线程执行完毕后,主程序也结束了,并且后台线程也被终止了,这就是前台线程和后台线程的区别:进程会等待所有的前台进程执行完毕后才结束,单不会等待后台进程执行完毕。

    八、向线程传递参数

       在这一小节,我将讲述如何向一个线程传递参数,我们将使用不同的方法来完成这个工作,具体步骤如下所示:

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

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

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Recipe08
     7 {
     8     class ThreadSample
     9     {
    10         private readonly int iterations;
    11 
    12         public ThreadSample(int iterations)
    13         {
    14             this.iterations = iterations;
    15         }
    16 
    17         public void CountNumbers()
    18         {
    19             for(int i = 1; i <= iterations; i++)
    20             {
    21                 Sleep(TimeSpan.FromSeconds(0.5));
    22                 WriteLine($"{CurrentThread.Name} prints {i}");
    23             }
    24         }
    25     }
    26 
    27     class Program
    28     {
    29         static void Count(object iterations)
    30         {
    31             CountNumbers((int)iterations);
    32         }
    33 
    34         static void CountNumbers(int iterations)
    35         {
    36             for(int i = 1; i <= iterations; i++)
    37             {
    38                 Sleep(TimeSpan.FromSeconds(0.5));
    39                 WriteLine($"{CurrentThread.Name} prints {i}");
    40             }
    41         }
    42 
    43         static void PrintNumber(int number)
    44         {
    45             WriteLine(number);
    46         }
    47 
    48         static void Main(string[] args)
    49         {
    50             var sample = new ThreadSample(10);
    51 
    52             var threadOne = new Thread(sample.CountNumbers);
    53             threadOne.Name = "ThreadOne";
    54             threadOne.Start();
    55             threadOne.Join();
    56 
    57             WriteLine("--------------------------");
    58 
    59             var threadTwo = new Thread(Count);
    60             threadTwo.Name = "ThreadTwo";
    61             threadTwo.Start(8);
    62             threadTwo.Join();
    63 
    64             WriteLine("--------------------------");
    65 
    66             var threadThree = new Thread(() => CountNumbers(12));
    67             threadThree.Name = "ThreadThree";
    68             threadThree.Start();
    69             threadThree.Join();
    70 
    71             WriteLine("--------------------------");
    72 
    73             int i = 10;
    74             var threadFour = new Thread(() => PrintNumber(i));
    75 
    76             i = 20;
    77             var threadFive = new Thread(() => PrintNumber(i));
    78 
    79             threadFour.Start();
    80             threadFive.Start();
    81         }
    82     }
    83 }

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

      在第50~55行代码处,我们首先创建了一个ThreadSample类型的对象,并将数字10传递给了该类型的构造方法。然后我们将ThreadSample对象的“CountNumbers”方法传递给了线程threadOne的构造方法,“CountNumbers”方法将在线程threadOne中被执行,因此我们通过“CountNumbers”方法将数字10传递给了线程threadOne。

      在第59~62行代码处,我们使用“Thread.Start”的重载方法将一个对象传递给线程threadTwo,要使该段代码正确运行,我们要保证在构造threadTwo线程时,传给给Thread的构造方法的委托要带有一个object类型的参数。在这段代码中,我们将8看作一个对象传递给“Count”方法,该方法有调用了同名的重载方法将object对象转换为整数类型。

      在第66~69行代码处,我们在创建threadThree线程时,给Thread构造方法传递的是一个lambda表达式,该表达式定义了一个不属于任何类的方法,我们在该lambda表达式中调用了“CountNumbers”方法,并将12传递给了“CountNumbers”方法,通过lambda表达式,我们将12传递给了threadThree线程。

      注意:当我们将一个局部变量用于多个lambda表达式时,这些lambda表达式将会共享这个变量的值,在第73~80行代码处,我们将局部变量用于了两个lambda表达式,我们运行threadFour和threadFive线程后,我们发现打印出来的结果都是20。

       未完待续!

  • 相关阅读:
    有几个点会导致插件包不出现
    JScrollPane恢复正常滚动量
    java jlabel 对齐_java将JLabel中的文本右对齐
    SVN的安装和使用手册
    idea生成类注释和方法注释的正确方法
    关于RCP中英文的一些总结
    精通Hibernate类与类关联关系:[一]建立多对一的单向关联关系
    Hibernate笔记=>继承关系的映射[转 王继彬]
    Unity(六):使用场景Ⅲ:用于依赖注入(上)
    NHibernate之旅(13):初探立即加载机制[转 Blog 李永京]
  • 原文地址:https://www.cnblogs.com/yonghuacui/p/6186242.html
Copyright © 2020-2023  润新知