C# 线程,网络编程与Linq知识点
📊 文章知识结构思维导图
以下是本文核心知识点的思维导图,帮助您快速建立知识框架:

一. 线程
线程:进程内部执行流最小单元,一个进程可以有多条线程; 线程共享进程堆内存,每个线程拥有独立栈内存(默认约 1MB)。
. 线程生命周期
- 新建 New:
new Thread(方法),仅创建对象,未运行 - 就绪 Ready:调用
Start(),交给操作系统等待 CPU 调度 - 运行 Running:CPU 分配时间片,真正执行代码
- 阻塞 / 休眠:Sleep、等待锁、IO,让出 CPU
- 终止 Stopped:委托执行完毕 / 异常退出,线程结束,不可重启。

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

重点熟记:
前台线程:(适用场景:必须完整执行完的业务任务。)
只要还有任意一个前台线程在运行,应用程序进程就不会退出。 即使主线程执行完毕,如果前台线程还在干活,程序依旧保持运行;只有所有前台线程全部结束,进程才会关闭。
后台线程:(适用场景:次要辅助任务,日志打印、状态轮询、监控,程序退出可以直接中断的工作。)
后台线程不阻止进程退出。当所有前台线程全部结束,程序进程直接关闭,后台线程会被操作系统强行终止,不管任务有没有做完。
线程使用:
-
创建线程对象
-
调用Start()方法启动线程
-
这个线程,自动与其他线程一起抢占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求和,因此将求和写为方法,建立两个线程调用方法实现两个线程同时求和要求。
错误点:线程串行执行,没有并行。并没有按照题目要求实现。
- 启动 t1 →
Join()卡住主线程,等待 t1 执行完毕 - t1 全部执行结束后,才创建、启动 t2 , t1、t2 是先后运行,不是同时并发执行,违背多线程并行的题目要求。
- 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("程序结束!");
}
}
}
- 正确做法:先调用所有线程的
Start()方法把两个线程全部启动,再依次调用Join()等待所有线程执行完成。 - 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)时,七层模型如何运行?
-
应用层(7层):浏览器发起 HTTP 请求:构造 HTTP 报文 , 需要 DNS 解析:把域名
www.example.com翻译成服务器 IP 地址。 -
表示层(6 层) 确定数据编码格式,处理字符编码。
-
会话层(5 层) 建立客户端与服务器之间的会话连接。
-
传输层(4 层) 选用TCP 协议。浏览器随机源端口。 加上 TCP 头部,封装成 TCP 段。(三次握手先建立 TCP 连接)
-
网络层(3 层) 加上 IP 头部,填入本机 IP(源 IP)、服务器 IP(目的 IP)。生成 IP 数据包。路由器根据 IP 做路由转发,经过多个网络。
-
数据链路层(2 层) 封装帧头部,下一跳路由器 MAC 地址。
-
物理层(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的区别:
- TCP 是面向连接的可靠传输协议,通信前需要三次握手建立连接,提供重传、流量控制,保证数据完整有序到达,但是开销大速度慢,适合对可靠性要求高的场景。
- 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.解决:理清筛选逻辑,看清要求。把握筛选数据书写地点。总之多思考问题要求,把握语法合理运用。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)