• C#【高级篇】 C# 多线程


    C#学习汇总 - 总目录


    前言

    线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作

    线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率

    到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程


    一、线程生命周期

    线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。

    下面列出了线程生命周期中的各种状态:

    • 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。

    • 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。

    • 不可运行状态:下面的几种情况下线程是不可运行的:

      已经调用 Sleep 方法
      已经调用 Wait 方法
      通过 I/O 操作阻塞

    • 死亡状态:当线程已完成执行或已中止时的状况。

    二、主线程

    在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程

    当 C# 程序开始执行时,主线程自动创建。使用 Thread 类创建的线程被主线程的子线程调用。您可以使用 Thread 类的 CurrentThread 属性访问线程

    下面的程序演示了主线程的执行:

    using System;
    using System.Threading;
    
    namespace MultithreadingApplication
    {
        class MainThreadProgram
        {
            static void Main(string[] args)
            {
                Thread th = Thread.CurrentThread;
                th.Name = "MainThread";
                Console.WriteLine("This is {0}", th.Name);
                Console.ReadKey();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行结果:

    This is MainThread
    
    • 1

    三、创建线程

    线程是通过扩展 Thread 类创建的。扩展的 Thread 类调用 Start() 方法来开始子线程的执行。

    下面的程序演示了这个概念:

    using System;
    using System.Threading;
    
    namespace MultithreadingApplication
    {
        class ThreadCreationProgram
        {
            public static void CallToChildThread()
            {
                Console.WriteLine("Child thread starts");
            }
           
            static void Main(string[] args)
            {
                //【写法1】:ThreadStart、Thread都使用
                ThreadStart childref = new ThreadStart(CallToChildThread);
                Console.WriteLine("In Main: Creating the Child thread");
                Thread childThread = new Thread(childref);
             
             	//【写法2】只使用Thread。在2.0以后可以像下边这样写,程序会更简单易懂点
      				//Thread childThread = new Thread(CallToChildThread);
                childThread.Start();
                Console.ReadKey();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    运行结果:

    In Main: Creating the Child thread
    Child thread starts
    
    • 1
    • 2

    特别说明:

    下边两种写法的效果是一样的。都是创建一个线程。后者只是 C# 的语法,编译时编译器会自动转换成第一种的形式。ThreadStart 是线程的入口,可以理解为一个函数指针,指向线程将要运行的函数

    Thread childThread = new Thread( new ThreadStart(CallToChildThread));
    
    Thread childThread = new Thread(CallToChildThread);
    
    • 1
    • 2
    • 3

    四、管理线程【Thread.Sleep(1000) 线程暂停1s】

    Thread 类提供了各种管理线程的方法。

    下面的实例演示了 sleep() 方法的使用,用于在一个特定的时间暂停线程。

    using System;

    using System.Threading;
    
    namespace MultithreadingApplication
    {
        class ThreadCreationProgram
        {
            public static void CallToChildThread()
            {
                Console.WriteLine("Child thread starts");
                // 线程暂停 5000 毫秒
                int sleepfor = 5000;
                Console.WriteLine("Child Thread Paused for {0} seconds",
                                  sleepfor / 1000);
                Thread.Sleep(sleepfor);
                Console.WriteLine("Child thread resumes");
            }
           
            static void Main(string[] args)
            {
                ThreadStart childref = new ThreadStart(CallToChildThread);
                Console.WriteLine("In Main: Creating the Child thread");
                Thread childThread = new Thread(childref);
                childThread.Start();
                Console.ReadKey();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    运行结果:

    In Main: Creating the Child thread
    Child thread starts
    Child Thread Paused for 5 seconds
    Child thread resumes
    
    • 1
    • 2
    • 3
    • 4

    五、销毁线程【Abort()】

    Abort() 方法用于销毁线程。

    通过抛出 threadabortexception 在运行时中止线程。这个异常不能被捕获,如果有 finally 块,控制会被送至 finally 块。

    下面的程序说明了这点:

    using System;
    using System.Threading;
    
    namespace MultithreadingApplication
    {
        class ThreadCreationProgram
        {
            public static void CallToChildThread()
            {
                try
                {
    
                    Console.WriteLine("Child thread starts");
                    // 计数到 10
                    for (int counter = 0; counter <= 10; counter++)
                    {
                        Thread.Sleep(500);
                        Console.WriteLine(counter);
                    }
                    Console.WriteLine("Child Thread Completed");
    
                }
                catch (ThreadAbortException e)
                {
                    Console.WriteLine("Thread Abort Exception");
                }
                finally
                {
                    Console.WriteLine("Couldn't catch the Thread Exception");
                }
    
            }
           
            static void Main(string[] args)
            {
                ThreadStart childref = new ThreadStart(CallToChildThread);
                Console.WriteLine("In Main: Creating the Child thread");
                Thread childThread = new Thread(childref);
                childThread.Start();
                // 停止主线程一段时间
                Thread.Sleep(2000);
                // 现在中止子线程
                Console.WriteLine("In Main: Aborting the Child thread");
                childThread.Abort();
                Console.ReadKey();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    当上面的代码被编译和执行时,它会产生下列结果:

    In Main: Creating the Child thread
    Child thread starts
    0
    1
    2
    In Main: Aborting the Child thread
    Thread Abort Exception
    Couldn't catch the Thread Exception 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总结

    • 进程和线程的理解:进程(一个程序)、线程(并发时多个分支程序)、主线程(Main)
    • 多线程的常见使用:Sleep、Abort、Resume
    • 线程池和Task简单了解

    补充1【Thread、线程池ThreadPool、Task】

    C# 4.0 以后一共有3种创建线程的方式:

    • ==Thread ==自己创建的独立的线程, 优先级高,需要使用者自己管理。
    • ==ThreadPool ==有 .Net 自己管理, 只需要把需要处理的方法写好, 然后交个.Net Framework, 后续只要方法执行完毕, 则自动退出。
    • Task 4.0 以后新增的线程操作方式, 类似 ThreadPool, 但效率测试比ThreadPool略高, Task对多核的支持更为明显,所以在多核的处理器中, Task的优势更为明显

    实例:

    using System;
    using System.Threading;
    
    namespace ConsoleApp2
    {
        class Program
        {
            public static Thread t =null;
            static void Main(string[] args)
            {  
                //【01】独立创建线程
                t = new Thread(ThreadProcess1);
                t.Start(new object());
    
                //【02】线程池
                ThreadPool.QueueUserWorkItem(ThreadProcess2, new object());
                //【03】Task方式创建线程
                System.Threading.Tasks.Task.Factory.StartNew(ThreadProcess3, new object());
    
                Console.ReadLine();
            }
            private static void ThreadProcess1(object tag)
            {
                int i = 10;
                while (i > 0)
                {
                    Console.WriteLine(string.Format("【01】i:{0} ", i));
                    Thread.Sleep(10);
                    i--;
                }
                Console.WriteLine("【01】线程结束");
    
                //需要手动终止
                t.Abort();
    
            }
            private static void ThreadProcess2(object tag)
            {
                int i = 10;
                while (i > 0)
                {
                    Console.WriteLine(string.Format("【02】i:{0} ", i));
                    Thread.Sleep(10);
                    i--;
                }
                Console.WriteLine("【02】线程结束");
    
            }
            private static void ThreadProcess3(object tag)
            {
                int i = 10;
                while (i > 0)
                {
                    Console.WriteLine(string.Format("【03】i:{0} ", i));
                    Thread.Sleep(10);
                    i--;
                }
                Console.WriteLine("【03】线程结束");
    
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    运行结果:
    在这里插入图片描述

    补充2【ThreadStart和ParameterizedThreadStart】

    线程函数通过委托传递,可以不带参数,也可以带参数(只能有一个参数),可以用一个类或结构体封装参数:

    using System;
    using System.Threading;
    
    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                Thread t1 = new Thread(new ThreadStart(TestMethod));
                Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
                t1.IsBackground = true;
                t2.IsBackground = true;
                t1.Start();
                t2.Start("hello");
                Console.ReadKey();
            }
    
            public static void TestMethod()
            {
                Console.WriteLine("不带参数的线程函数");
            }
    
            public static void TestMethod(object data)
            {
                string datastr = data as string;
                Console.WriteLine("带参数的线程函数,参数为:{0}", datastr);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    运行结果:

    不带参数的线程函数
    带参数的线程函数,参数为:hello
    
    • 1
    • 2

    C#学习汇总 - 总目录

  • 相关阅读:
    03-视频点播-文件上传及媒资管理
    【一起来学C++】————(5)内存管理(上)
    牛客 NC25080 Catch That Cow
    java毕业设计花店管理系统(附源码、数据库)
    Vue语法
    jeecg框架报Unable to scan documentation context default错误
    python is not set from command line or npm configuration
    java基础巩固8
    【matplotlib基础】--3D图形
    JS数据类型的判断方式
  • 原文地址:https://blog.csdn.net/sinat_40003796/article/details/125516263