📊 文章知识结构思维导图

以下是本文核心知识点的思维导图,帮助您快速建立知识框架:

一. 线程

线程:进程内部执行流最小单元,一个进程可以有多条线程; 线程共享进程堆内存,每个线程拥有独立栈内存(默认约 1MB)。

. 线程生命周期

  1. 新建 Newnew Thread(方法),仅创建对象,未运行
  2. 就绪 Ready:调用Start(),交给操作系统等待 CPU 调度
  3. 运行 Running:CPU 分配时间片,真正执行代码
  4. 阻塞 / 休眠:Sleep、等待锁、IO,让出 CPU
  5. 终止 Stopped:委托执行完毕 / 异常退出,线程结束,不可重启。

.

线程类 Thread

线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程。

重点熟记

前台线程:(适用场景:必须完整执行完的业务任务。)

只要还有任意一个前台线程在运行,应用程序进程就不会退出。 即使主线程执行完毕,如果前台线程还在干活,程序依旧保持运行;只有所有前台线程全部结束,进程才会关闭。

后台线程:(适用场景:次要辅助任务,日志打印、状态轮询、监控,程序退出可以直接中断的工作。)

后台线程不阻止进程退出。当所有前台线程全部结束,程序进程直接关闭,后台线程会被操作系统强行终止,不管任务有没有做完。

线程使用:

  1. 创建线程对象

  2. 调用Start()方法启动线程

  3. 这个线程,自动与其他线程一起抢占CPU时间片, 执行它绑定方法的代码

线程类 Thread的常用方法

Start([object obj]) 启动线程,并把线程状态更改为 Running
Abort([object obj]) 开始终止此线程的过程。
Join()/Join(int millisecondsTimeout)/Join(TimeSpan timeout) 当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程
static Yield() 当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行.

Task 类: 异步编程

  • 使用线程池: ThreadPool

一个线程,一定要调用Start()方法, 线程启动, 线程才可以进入抢占CPU资源队伍中

一个线程一旦启动之后, 不能重复启动

Thread、ThreadPool、Task 对比(重点)

代码案例:

编写一个程序,使用两个线程分别计算1到100的和
namespace App8_18
{
    internal class Program
    {
        public static void Sum()
        {
            int sum = 0;
            for (int i = 1; i <= 100; i++)
            {
               sum = sum + i;
            }
            Console.WriteLine($"计算已完成,计算结果为{sum}");
        }
        static void Main(string[] args)
        {
            Thread t1 = new Thread(Sum);
            t1.Name = "线程A";
            Console.WriteLine ($"{t1.Name}开始执行");
            t1.Start();
            t1.Join();
            Thread t2 = new Thread(Sum);
            t2.Name = "线程B";
            Console.WriteLine($"{t2.Name}开始执行");
            t2.Start();
            t2.Join();
            Console.WriteLine("程序结束!");

        }
    }
}

解题思路:题目要求两个线程都实现1-100求和,因此将求和写为方法,建立两个线程调用方法实现两个线程同时求和要求。

错误点:线程串行执行,没有并行。并没有按照题目要求实现。

  1. 启动 t1 → Join()卡住主线程,等待 t1 执行完毕
  2. t1 全部执行结束后,才创建、启动 t2 , t1、t2 是先后运行,不是同时并发执行,违背多线程并行的题目要求。
  3. Sum 方法内部 sum 是局部变量。

正确代码:

namespace App8_18
{
    internal class Program
    {
        public static void Sum()
        {
            int sum = 0;
            for (int i = 1; i <= 100; i++)
            {
                sum = sum + i;
            }
            Console.WriteLine($"计算已完成,计算结果为{sum}");
        }
        static void Main(string[] args)
        {
            Thread t1 = new Thread(Sum);
            t1.Name = "线程A";
            Console.WriteLine($"{t1.Name}开始执行");
            t1.Start();

            Thread t2 = new Thread(Sum);
            t2.Name = "线程B";
            Console.WriteLine($"{t2.Name}开始执行");
            t2.Start();

            // 全部Start之后,再统一Join等待,实现并行
            t1.Join();
            t2.Join();

            Console.WriteLine("程序结束!");
        }
    }
}
  1. 正确做法:先调用所有线程的Start()方法把两个线程全部启动,再依次调用Join()等待所有线程执行完成。
  2. Sum 方法中 sum 为方法局部变量,存放在线程栈,每个线程拥有独立副本,该部分不存在线程安全问题。

二.网络编程

核心要素:

1.协议:约束数据的发收格式。

2.IP地址:数据发送的目标设备

3.端口:标识数据要发送到的目标设备应用

OSI 七层参考模型

从上到下:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

层数 名称 主要功能 典型协议 / 设备
7 应用层 为应用程序提供网络服务,直接给软件提供接口 HTTP、HTTPS、FTP、DNS、SMTP
6 表示层 数据格式转换、加密解密、压缩解压缩,处理编码 JPEG、ASCII、加密解密
5 会话层 建立、管理、断开会话连接,维持会话 NetBIOS
4 传输层 端到端的数据传输,差错控制、流量控制 TCP、UDP;端口
3 网络层 寻址、路由选择,跨网段转发数据包 IP、ARP;路由器
2 数据链路层 把数据包封装成帧,相邻节点传输,差错检测 以太网、MAC 地址;交换机
1 物理层 传输比特流(0/1 电信号),硬件介质 网线、光纤、网卡、集线器

扩展知识点:

当您在浏览器中输入一个网址(如 http://www.example.com)时,七层模型如何运行?

  1. 应用层(7层):浏览器发起 HTTP 请求:构造 HTTP 报文 , 需要 DNS 解析:把域名www.example.com翻译成服务器 IP 地址。

  2. 表示层(6 层) 确定数据编码格式,处理字符编码。

  3. 会话层(5 层) 建立客户端与服务器之间的会话连接。

  4. 传输层(4 层) 选用TCP 协议。浏览器随机源端口。 加上 TCP 头部,封装成 TCP 段。(三次握手先建立 TCP 连接)

  5. 网络层(3 层) 加上 IP 头部,填入本机 IP(源 IP)、服务器 IP(目的 IP)。生成 IP 数据包。路由器根据 IP 做路由转发,经过多个网络。

  6. 数据链路层(2 层) 封装帧头部,下一跳路由器 MAC 地址。

  7. 物理层(1 层) 把帧转为比特流 ,通过网线 等等向外发送。

TCP通信

TCP(Transmission Control Protocol,传输控制协议)是互联网协议套件(TCP/IP)中最重要、最核心的协议之一,它提供了可靠的、面向连接的、基于字节流的端到端通信服务。

核心重点:

TCP三次握手协议

目的:确保客户端和服务端双发都具备接收数据和发送数据的能力。

核心方法:

方法 作用 关键说明
UdpClient() 构造,创建 UdpClient 对象 不绑定本地端口,发送端使用,系统自动分配端口
UdpClient(int port) 构造,绑定本机端口 接收端使用,监听该端口接收 UDP 数据
int Send(byte[] data, int length, IPEndPoint remoteEp) 发送 UDP 报文 参数:数据、长度、目标端点;UDP 无连接,直接指定接收方
byte[] Receive(ref IPEndPoint remoteEp) 接收 UDP 报文 阻塞方法;ref 参数输出发送方 IP 与端口;返回接收到的字节数组
Close() 关闭 UdpClient 释放套接字资源
Dispose() 释放资源 using 语句会自动调用

TCP案例

创建一个简单的TCP服务器和客户端, 服务器接收客户端发送的消息,并将消息原样返回给客户端。



//客户端
//先发后收
namespace App8_20
{
    internal class Program
    {
        static void Main(string[] args)
        {
           //创建tcp对象
           TcpClient tcpClient = new TcpClient();
            try
            {
                //链接服务器
                tcpClient.Connect(IPAddress.Loopback,11111);
                //获取字节流
                Stream stream = tcpClient.GetStream();
                //发送信息
                Console.WriteLine("请输入信息:");
                string input = Console.ReadLine();
                byte[] date = Encoding.UTF8.GetBytes(input);
                stream.Write(date,0,date.Length);
                byte[] b = new byte[1024];
                int len = stream.Read(b, 0, b.Length);
                //输出
                string content = Encoding.UTF8.GetString(b, 0, len);
                Console.WriteLine($"[服务端回复]:{content}");

                stream.Flush();

                tcpClient.Close();
                Console.ReadKey();
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }

        }
    }
}
//服务端
namespace App8_20_1
{
    internal class Program
    {
        static void Main(string[] args)
        {   
           
            TcpListener listener = null;
            try 
            {   //创建监听器对象
                listener = new TcpListener(IPAddress.Loopback, 11111);
                //启动监听
                listener.Start();
                Console.WriteLine($"服务器启动成功,监听端口:{((IPEndPoint)listener.LocalEndpoint).Port}");
                //3.等待客户端连接
                TcpClient tcpClient = listener.AcceptTcpClient();//阻塞方法
                //接收信息
                Stream stream = tcpClient.GetStream();
                //先收后发
                byte[] b = new byte[1024];
                int len = stream.Read(b, 0, b.Length);
                //输出
                string content = Encoding.UTF8.GetString(b, 0, len);
                Console.WriteLine($"[客户端]:{content}");
                //发送
                //Console.WriteLine("+++++++++");
                //string inpout = Console.ReadLine();
                byte[] date = Encoding.UTF8.GetBytes(content);
                stream.Write(date, 0, date.Length);
                stream.Flush();
               stream.Close();
                tcpClient.Close();

            }
            catch (Exception e) 
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

UDP通信

UDP是无连接通信协议, 数据传输时,数据的发送端和接收端不建立逻辑连接。

核心方法
方法 描述
Send(byte[] data, int length, IPEndPoint remoteEP) 发送数据报到指定的远程端点。
Send(byte[] data, int length, string hostname, int port) 通过主机名和端口发送数据报。
Receive(ref IPEndPoint remoteEP) 阻塞并接收数据报,返回数据字节数组,并通过 ref 参数返回发送方的端点信息。
SendAsync() 基于任务的异步模式(TAP)的发送方法。
ReceiveAsync() 基于任务的异步模式(TAP)的接收方法。
JoinMulticastGroup(IPAddress multicastAddr) 加入多播组,用于接收多播数据。
Close() / Dispose() 关闭连接并释放资源。
扩展知识:

UDP与TCP的区别:

  1. TCP 是面向连接的可靠传输协议,通信前需要三次握手建立连接,提供重传、流量控制,保证数据完整有序到达,但是开销大速度慢,适合对可靠性要求高的场景。
  2. UDP 是无连接的不可靠传输协议,无需建立连接,直接发送报文,没有重传机制,开销小传输速度快,适合实时性要求高,可以容忍少量丢包的场景。

三.Linq语言集成查询

LINQ 的全称是 Language Integrated Query(语言集成查询)。它允许你使用类似 SQL 的查询语法,直接在 C# 代码中对各种数据源(如集合、数据库、XML 等)进行查询和操作。

核心知识点:

Linq查询语法与Linq方法语法:

LINQ 查询语法(查询表达式,声明式)

也叫查询表达式语法,类似 SQL 语句风格,使用from、where、select、orderby、group by这些关键字编写。

  • 写法接近 SQL,可读性好,适合复杂多条件查询。

代码案例:

var res = from s in list
          where s.Age>18
          orderby s.Name
          select s;

LINQ 方法语法

也叫扩展方法语法,调用Where()、Select()、OrderBy()等扩展方法,使用 Lambda 表达式。

  • 使用链式调用,多个方法连续点调用。
  • 灵活,适合简单查询,代码紧凑。
  • 所有查询语法都可以改写为方法语法

代码案例:

var res = list.Where(s=>s.Age>18).OrderBy(s=>s.Name).Select(s=>s);

LINQ 标准查询运算符

1. 筛选
  • Where:按条件过滤元素
  • OfType<T>:按类型筛选集合元素
2. 投影(选择)
  • Select:投影转换,返回指定字段 / 对象
  • SelectMany:把嵌套集合展开,扁平化
3. 排序
  • OrderBy:升序
  • OrderByDescending:降序
  • ThenBy:次要升序
  • ThenByDescending:次要降序
4. 分组
  • GroupBy:按键分组
5. 连接
  • Join:内连接
  • GroupJoin:分组左连接
6. 集合运算
  • Distinct:去重
  • Union:并集
  • Intersect:交集
  • Except:差集
7. 分区(截取)
  • Take(n):取前 n 条
  • Skip(n):跳过 n 条
  • TakeWhile:满足条件就取
  • SkipWhile:满足条件就跳过
8. 元素操作(返回单个元素)
  • First() / FirstOrDefault():第一个
  • Last() / LastOrDefault():最后一个
  • Single() / SingleOrDefault():唯一一个
  • ElementAt() / ElementAtOrDefault():指定索引元素
9. 聚合运算符(返回一个值)
  • Count():数量
  • Sum():求和
  • Max():最大值
  • Min():最小值
  • Average():平均值
10. 判断(返回 bool)
  • Any():是否存在满足条件元素
  • All():全部元素是否满足条件
  • Contains():是否包含指定元素
11. 转换
  • ToList():转为 List 集合
  • ToArray():转为数组
  • ToDictionary():转为字典

常见错误与解决方法

1.共享数据错乱 多个线程同时读写同一个共享变量,造成数据结果错误。银行取款错误,成绩查询错误等等。

2.解决:使用 lock 关键字加锁,保证同一时刻只有一个线程访问共享数据;尽量避免多线程修改共享变量,也可以使用线程安全集合。

private static readonly object lockObj = new object();




lock (锁对象)
{
    //临界区代码:多线程访问共享数据的代码
}

1.程序运行卡死(进入死循环):while 循环的布尔退出条件没有被正确修改;忘记写循环变量自增;标记变量被多线程修改出现可见性问题,线程看不到标记的变化,一直循环。

bool flag = true;
while(flag)
{
    //逻辑,没有修改flag
}

2.解决:检查循环退出条件,及时修改布尔条件;使用布尔标记控制循环退出。

1.Linq语法查询逻辑错误:语法筛选逻辑书写地点错误导致筛选出结果并不是题目要求。

2.解决:理清筛选逻辑,看清要求。把握筛选数据书写地点。总之多思考问题要求,把握语法合理运用。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐