【C#补全计划】多线程

一、进程

  1. 概念:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础

  2. 进程之间可以相互独立运行、互不干扰,也可以相互访问、操作

二、线程

  1. 概念:操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位

  2. 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程

三、多线程的语法

  1. 类:Thread

  2. 引用命名空间:using System.Threading;

  3. 声明一个新线程:Thread 线程名 = new Thread();

cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadMethod1);
        }

        static void threadMethod1()
        {
            Console.WriteLine("线程1的逻辑...");
        }
    }
}
  1. 启动线程:线程名.Start();
cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadMethod1);
            
            // 启动线程
            t1.Start();
        }

        static void threadMethod1()
        {
            Console.WriteLine("运行线程1的逻辑...");
        }
    }
}
  1. 设置为后台线程:线程名.IsBackground = true;

(1)当所有前台线程结束的时候,整个程序就结束了。无论是否还有后台线程在运行。

(2)后台线程会随着应用程序进程的终止而终止

(3)如果不设置为后台线程,可能导致进程无法正常关闭

(4)若线程1无限循环:

cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadMethod1);
            
            // 启动线程
            t1.Start();
        }

        static void threadMethod1()
        {
            while (true) {
                Console.WriteLine("运行线程1的逻辑...");
            }
        }
    }
}

运行结果如下:(无限循环执行)

(5)设置线程1为后台线程:

cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadMethod1);
            
            // 启动线程
            t1.Start();
            
            // 设置为后台线程
            t1.IsBackground = true;

            for (int i = 0; i < 10; ++i) {
                Console.WriteLine("运行主线程的逻辑...");
            }
        }

        static void threadMethod1()
        {
            while (true) {
                Console.WriteLine("运行线程1的逻辑...");
            }
        }
    }
}

运行结果如下:(主线程结束后线程1也结束)

  1. 释放线程:

(1)如果开启的线程不是死循环,不需要刻意去关闭

(2)如果开启的线程是死循环,有两种关闭方式:

①设置bool标识

cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        static bool isRuning = true;
        
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadMethod1);
            
            // 启动线程
            t1.Start();
            
            // 设置为后台线程
            t1.IsBackground = true;
            
            for (int i = 0; i < 10; ++i) {
                Console.WriteLine("运行主线程的逻辑...");
            }
            
            // 释放线程
            // 1.设置bool标识
            isRuning = false;
        }

        static void threadMethod1()
        {
            while (isRuning) {
                Console.WriteLine("运行线程1的逻辑...");
            }
        }
    }
}

运行结果如下:

②调用线程提供的Abort方法

cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        // static bool isRuning = true;
        
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadMethod1);
            
            // 启动线程
            t1.Start();
            
            // 设置为后台线程
            t1.IsBackground = true;
            
            for (int i = 0; i < 10; ++i) {
                Console.WriteLine("运行主线程的逻辑...");
            }
            
            // 释放线程
            // 1.设置bool标识
            // isRuning = false;
            
            // 2.使用线程的Abort方法
            t1.Abort();
        }

        static void threadMethod1()
        {
            while (true) {
                Console.WriteLine("运行线程1的逻辑...");
            }
        }
    }
}

运行结果同上。

注意:在.Net core版本中无法中止,会发生报错

  1. 休眠线程:Thread.Sleep();
cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        // static bool isRuning = true;
        
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadMethod1);
            
            // 启动线程
            t1.Start();
            
            // 设置为后台线程
            t1.IsBackground = true;
            
            for (int i = 0; i < 10; ++i) {
                Console.WriteLine("运行主线程的逻辑...");
            }
            
            // 释放线程
            // 1.设置bool标识
            // isRuning = false;
            
            // 2.使用线程的Abort方法
            t1.Abort();
            
            // 休眠线程
            // 在哪个函数中调用,就使哪个线程休眠
            Thread.Sleep(1000); // 使线程休眠1000毫秒 1秒=1000毫秒
        }

        static void threadMethod1()
        {
            while (true) {
                Console.WriteLine("运行线程1的逻辑...");
            }
        }
    }
}

四、线程之间共享数据

  1. 多个线程之间使用的内存使共享的,都属于应用程序(进程)

  2. 要注意:当多线程同时操作同一片内存区域时可能会出问题,但是可以通过加锁的形式避免

  3. 关键字:lock

  4. 作用:在多个线程想要访问相同的内存时,实现共享资源的安全访问

  5. 语法:

lock(引用类型对象){

// 代码块

}

  1. 实现:在(0,0)处绘制一个红色的圆形、在(10,5)处绘制一个黄色的正方形

代码:

cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        // static bool isRuning = true;
        
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadDrawCir);
            Thread t2 = new Thread(threadDrawRec);
            
            // 启动线程
            t1.Start();
            t2.Start();
            
            // 设置为后台线程
            t1.IsBackground = true;
            t2.IsBackground = true;
            
            // 释放线程
            // 1.设置bool标识
            // isRuning = false;
            
            // 2.使用线程的Abort方法
            // t1.Abort();
            
            // 休眠线程
            // 在哪个函数中调用,就使哪个线程休眠
            // Thread.Sleep(1000); // 使线程休眠1000毫秒 1秒=1000毫秒
            
            // 等待按键后结束前台进程
            Console.ReadKey();
        }

        static void threadDrawCir() // 在(0,0)处绘制一个红色的圆形
        {
            while (true) { 
                Console.SetCursorPosition(0, 0);
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("●");
            }
        }

        static void threadDrawRec() // 在(10,5)处绘制一个黄色的正方形
        {
            while (true) {
                Console.SetCursorPosition(10, 5);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("■");
            }
        }
    } 
}

运行结果如下;

出现该结果的原因是:因为两个线程是同时运行的,所以当在(0,0)处绘制一个红色的圆形后,没有将光标设置为(10,5),直接从绘制黄色的正方形开始运行。同理,在(10,5)处绘制一个黄色的正方形后,没有将光标设置为(0,0),直接从绘制红色的圆形开始运行。所以会出现上面的效果。

  1. 使用互斥锁:
cs 复制代码
using System;
using System.Threading;

namespace Multithreading
{
    class Program
    {
        // static bool isRuning = true;
        static object lockObj = new object(); // 创建一个互斥锁对象
        
        static void Main(string[] args)
        {
            // 声明一个新线程
            // 注意:线程执行的代码需要封装到一个新函数中
            Thread t1 = new Thread(threadDrawCir);
            Thread t2 = new Thread(threadDrawRec);
            
            // 启动线程
            t1.Start();
            t2.Start();
            
            // 设置为后台线程
            t1.IsBackground = true;
            t2.IsBackground = true;
            
            // 释放线程
            // 1.设置bool标识
            // isRuning = false;
            
            // 2.使用线程的Abort方法
            // t1.Abort();
            
            // 休眠线程
            // 在哪个函数中调用,就使哪个线程休眠
            // Thread.Sleep(1000); // 使线程休眠1000毫秒 1秒=1000毫秒
            
            // 等待按键后结束前台进程
            Console.ReadKey();
        }

        static void threadDrawCir() // 在(0,0)处绘制一个红色的圆形
        {
            while (true) {
                lock (lockObj) { // 使用互斥锁
                    Console.SetCursorPosition(0, 0);
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.Write("●");
                }
            }
        }

        static void threadDrawRec() // 在(10,5)处绘制一个黄色的正方形
        {
            while (true) {
                lock (lockObj) { // 使用互斥锁
                    Console.SetCursorPosition(10, 5);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("■");
                }
            }
        }
    } 
}

运行结果如下;

  1. 解释:访问lock中的代码块,会给该引用类型对象上锁;直到运行完所有lock代码块中的代码时,该引用类型对象才会解锁。在其他线程想要使用该引用对象时,若该对象已经上锁,则必须等待该对象解锁后才能继续运行。

五、多线程的意义

使用多线程专门处理一些复杂耗时的逻辑:比如寻路、网络通信等

今天的学习就到这里了。感谢阅读。

再见!

相关推荐
小溪学编程8 小时前
从 C++ 的规范变迁看语言发展
jvm·c++·面试
晚安code10 小时前
JVM 类加载机制详解:双亲委派模型的原理、优势与三种打破方式
jvm
Joe_Wang512 小时前
【从0到1学习JVM · 21】既不浪费空间又没有碎片,标记整理算法到底慢在哪
jvm·学习·算法·垃圾回收·标记整理算法
Cheney Pan13 小时前
第4篇 Spring Boot 应用监控:Micrometer 与 JVM 指标
jvm·spring boot·后端·prometheus
Joe_Wang514 小时前
【从0到1学习JVM · 25】同样都要停顿所有线程,Parallel比Serial到底强在哪
java·jvm·学习·垃圾回收
wuminyu14 小时前
JVM虚拟线程的底层实现原理分析
java·linux·c语言·jvm·c++
wuminyu1 天前
C++协程实现接收端的零拷贝Buffer管理原理剖析
java·linux·c语言·jvm·c++
波力海苔夹心脆6751 天前
C# 值类型与引用类型详解:存储位置、赋值机制、参数传递、相等比较、装箱拆箱与常见陷阱
开发语言·jvm·经验分享·笔记·c#·.net
Joe_Wang51 天前
【从0到1学习JVM · 15】对象在堆里来回倒腾,究竟怎样才会进老年代?
java·jvm·学习·垃圾回收
小宋10211 天前
SQL + RAG 混合问答实战:结构化指标与文档证据如何统一路由
java·jvm·人工智能·sql