第六周学习内容
本周学习了线程与进程、UDP与TCP协议、Linq查询语法
注:本周思维导图

第一章 进程与线程
一、进程
1.1 什么是进程
进程: 运行时的程序, 进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。
程序运行时, 操作系统会为他创建进程
在操作系统中进程是进行系统资源分配、调度和管理的最小单位,进程在执行过程中拥有独立的内存单元。
每一个进程有自己独立内存空间
1.2 进程类
Process 获取操作系统所有运行进程信息, 以及杀死进程, 启动进程(运行程序)
场景: 1. 监控系统运行状况 2. 运行其他程序
属性
| 属性 | 说明 |
|---|---|
| MachineName | 获取关联进程正在其上运行的计算机的名称 |
| Id | 获取关联进程的唯一标识符 重要: pid 进程id |
| ExitTime | 获取关联进程退出的时间 |
| ProcessName | 获取该进程的名称 重要 |
| StartTime | 获取关联进程启动的时间 |
| Threads | 获取在关联进程中运行的一组线程 |
| TotalProcessorTime | 获取此进程的总的处理器时间 |
| UserProcessorTime | 获取此进程的用户处理器时间 |
| HasExited | 进程是否已终止 |
| ExitCode | 获取进程终止的编码 |
| BasePriority | 获取进程的基本优先级 |
方法
| 方法 | 说明 |
|---|---|
| Close() | 释放与此组件关联的所有资源 |
| CloseMainWindow() | 通过向进程的主窗口发送关闭消息来关闭拥有用户界面的进程 |
| Dispose() | 释放由 Component 使用的所有资源 |
| GetCurrentProcess() | 获取新的 Process 组件,并将其与当前活动的进程关联 |
| GetProcesses() | 为本地计算机上的每个进程资源创建一个新的 Process 组件 |
| GetProcesses(String) | 为指定计算机上的每个进程资源创建一个新的 Process 组件 |
| GetProcessesByName(String) | 创建新的 Process 组件的数组,并将它们与本地计算机上共享指定的进程名称的所有进程资源关联 |
| Kill() | 立即停止关联的进程 |
| Start() | 启动(或重用)此 Process 组件的 Startinfo 属性指定的进程资源, 并将其与该组件关联 |
| Start(String) | 通过指定文档或应用程序文件的名称来启动进程资源,并将资源与新的 Process 组件关联 |
| WaitForExit() | 等待关联进程退出 |
启动进程的方法
1、Start(), 需要先创建一个ProcessStartInfo对象,通过这个ProcessStartInfo设置启动进程相关信息, 把ProcessStartInfo对象赋值给Process的StartInfo属性上
//打开指定程序
ProcessStartInfo ps = new ProcessStartInfo(@"C:\Users\Administrator\Desktop\new.txt");
//通过一个进程打开一个指定的文件
Process p = new Process();
p.StartInfo = ps;
p.Start();
2、Start(string): 直接启动指定路径下的程序
//通过进程打开一些应用程序
//如果程序操作系统自带的程序,不需要完整路径, Path中,配置路径
//启动计算器
Process.Start("calc");
// 启动带参数的程序
//Process.Start("notepad", @"C:\temp\test.txt");
//使用ie浏览器打开网页
//Process.Start("iexplore", "http://www.baidu.com");
//打开微信, 写完整路径
Process.Start("C:\\Program Files\\Tencent\\Weixin\\Weixin.exe");
获取进程相关方法:
1、GetProcesses() 获取当前操作系统下所有进程
Process[] processes = Process.Getprocess();
foreach(var process in processes)
{
Console.Write($"进程名:{proces.ProcesName},进程ID:{process.ID}");
}
2.GetProcessesByName(String) 根据进程名获取指定进程
Process[] processes = Process.GetProcessesByName("WeChat");
if(processes.Length>0 && processes!=null)
{
Console.Write("$进程名:{process.ProcessName},进程Id:{process.Id}");
}
3.Process.GetProcessById() 根据进程id获取指定进程
Process p = Process.GetProcessById(10086);
4.获取当前进程, 获取代码所在程序的进程 GetCurrentProcess()
Process p1 = Process.GetCurrentProcess();
二、线程
2.1 什么是线程
线程 Thread: 进程中某个执行单元, 一个进程可以创建多个线程, 一个线程必须属于某个进程, 同一个进程中多个线程共同进程资源
线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源,多个线程共享内存,从而极大地提高了程序的运行效率。 线程抢占式的方式,抢占CPU资源
2.2 线程类
Task类:异步编程
线程池:ThreadPool
Thread类
常用属性:
| 属性 | 描述 |
|---|---|
| CurrentThread | 静态属性, 获取当前线程对象, 如果多个线程执行同一个方法, 在方法内部使用该属性得到执行代码的线程对象 |
| IsBackground | 实例属性, 设置/获取 线程是否是后台线程 |
| Name: | 设置/获取线程名字 |
| Priority | 设置/获取 线程优先级, 优先级高, 抢占cpu资源概率大 |
| ThreadState | 获取线程状态 |
面试题:
前台线程: IsBackground=false, 默认值, 可以阻止进程结束, 只要还有任何一个前台线程在运行,应用程序进程就不会结束
后台线程: IsBackground=true 不能阻止进程结束,当所有前台线程都结束时,进程会立即终止,不管后台线程是否还在运行。
常用方法:
| 方法 | 描述 |
|---|---|
| Start([object obj]) | 启动线程,并把线程状态更改为 Running |
| Abort([object obj]) | 开始终止此线程的过程。 |
| Join()/Join(int millisecondsTimeout)/Join(TimeSpan timeout) | 当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程 |
| static Yield() | 当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行. |
| Interrupt () | 中止处于 Wait或者Sleep或者Join 线程状态的线程; |
| static Sleep(int millisecondsTimeout/TimeSpan timeout) | 将当前线程挂起指定的时间 |
Thread thread = new Thread(()=>
for(int i=0; i<10 ;i++)
{
Console.WriteLine($"{thread.CurrentThread.ThreadName} 输出:{i}");
}
thread.Name = "线程1";
thread.Start();
thread.Join();
Console.WriteLine($"输出结束");
线程优先级:
优先级更高, 抢占CPU概率大, 针对某一次CPU资源, 优先级不一样一定抢占到
(从低到高)Lowest -> BelowNormal -> Normal ->AboveNormal -> Highest
2.3多线程并发访问
并发: 多个线程, 电脑只有一个CPU, cpu是单核, 是按照时间片切换实现同时执行
并行: 多个线程, 在多CPU, 多核, 多个线程, 在每个核独立执行,同时的
如果多个线程同时修改同一个资源,造成线程临时修改,丢失中间的临时值
解决方案:
使用lock锁, 线程锁, 互斥锁()
lock(锁对象){
//需要获取锁,才能执行的代码
}
lock锁的对象要求:
1、lock的是必须是引用类型的对象,string类型除外。
2、lock推荐的做法是使用静态的、只读的、私有的对象。
2.4 多线程场景面试题
多个线程读同一个文件, 每一个线程读到内容不一样, 多个线程 写文件

第二章 网络编程
一、网络
1.1 网络概念
网络: 电脑与电脑之间通信, 设备与设备之间的通信(数据)
分类:
1. 局域网
2.城域网
3.广域网
网络编程: 就是通过网络进行数据的传输。(设备与设备之间进行数据的传输)。又叫Scoket网络编程
分类:
-
UDP通信: 无连接
-
TCP通信: 有连接
1.2 七层网络协议

面试题:
当您在浏览器中输入一个网址(如 http://www.example.com)时,会发生以下过程:
-
应用层:浏览器生成一个HTTP请求(如“GET /index.html”)。
-
其下各层:这个HTTP请求会被依次封装(打包)到传输层的TCP段、网络层的IP包、数据链路层的帧中,最后通过物理层的比特流发送出去。
-
目标服务器:服务器收到数据后,会逆向地一层层解封装,最终将HTTP请求递交给Web服务器软件(如Apache, Nginx)。
-
响应:Web服务器软件处理请求,生成HTTP响应(如HTML页面),再按照同样的封装过程发回给您的浏览器。

网络通信三要素
-
协议 : 约束数据如何发送(约束数据发送的格式)
-
IP地址 : 标识数据要发送的目标设备
-
端口 : 标识数据要发送到目标设备的那个应用
二、UDP通信协议
核心特点
-
无连接:通信前不需要建立连接(如 TCP 的三次握手)。
-
不可靠:不保证数据包一定能到达目的地,也不保证顺序。
-
高效:没有连接建立和维护的开销,协议头更小,传输延迟更低。
-
面向数据报:数据是以一个个独立的包(数据报)为单位发送和接收的。
2.1 IpAddress : ip地址类
主要属性
| 属性 | 类型 | 描述 |
|---|---|---|
Address | IPAddress | 获取或设置终结点的 IP 地址。 |
Port | int | 获取或设置终结点的端口号。 |
AddressFamily | AddressFamily | 获取 IP 地址的地址族(例如 InterNetwork 对于 IPv4,InterNetworkV6 对于 IPv6)。这是一个继承自 EndPoint 的属性。 |
构造方法:
public IPEndPoint(IPAddress address, int port);
2.2 UDP核心类 UdpClient
UdpClient 是一个位于 System.Net.Sockets 命名空间中的高级辅助类,它简化了 UDP(用户数据报协议) 网络通信的编程过程。UDP 是一种无连接的、不可靠的但高效的数据传输协议。
服务器(Server)进程: 先收后发, 先收的要确定端口
常用构造函数
| 构造函数 | 描述 |
|---|---|
UdpClient() | 创建新的 UdpClient 实例,让系统自动分配可用端口。 |
UdpClient(int port) | 创建并绑定到指定的本地端口(用于接收)。 |
UdpClient(IPEndPoint localEP) | 创建并绑定到指定的本地端点(IP 和端口)。 |
UDP通信编码步骤:
-
创建UdpClient对象
-
调用UdpClient对象接收/发送方法
-
关闭资源 Close() UdpClient实现类IDisposable接口, 使用using语法糖,自动释放
核心方法
| 方法 | 描述 |
|---|---|
Send(byte[] data, int length, IPEndPoint remoteEP) | 发送数据报到指定的远程端点。 |
Send(byte[] data, int length, string hostname, int port) | 通过主机名和端口发送数据报。 |
Receive(ref IPEndPoint remoteEP) | 阻塞并接收数据报,返回数据字节数组,并通过 ref 参数返回发送方的端点信息。 |
BeginSend() / EndSend() | 异步发送的 Begin/End 模式(已过时,推荐 SendAsync)。 |
BeginReceive() / EndReceive() | 异步接收的 Begin/End 模式(已过时,推荐 ReceiveAsync)。 |
SendAsync() | 基于任务的异步模式(TAP)的发送方法。 |
ReceiveAsync() | 基于任务的异步模式(TAP)的接收方法。 |
JoinMulticastGroup(IPAddress multicastAddr) | 加入多播组,用于接收多播数据。 |
DropMulticastGroup(IPAddress multicastAddr) | 离开多播组。 |
Close() / Dispose() | 关闭连接并释放资源。 |
启动顺序: 先启动 先接收 后启动,先发
三、TCP通信协议
TCP(Transmission Control Protocol,传输控制协议)是互联网协议套件(TCP/IP)中最重要、最核心的协议之一,它提供了可靠的、面向连接的、基于字节流的端到端通信服务。
| 特性 | TCP描述 | 与 UDP 的对比 |
|---|---|---|
| 面向连接 | 通信双方必须先建立一条虚拟连接(三次握手),然后才能传输数据。通信结束后要断开连接(四次挥手)。 | UDP 是无连接的,直接发送。 |
| 可靠性 | 通过确认应答、超时重传、序列号等机制,确保数据不丢失、不重复、不乱序。 | UDP不保证可靠性,数据包可能丢失、重复或乱序。 |
| 基于字节流 | TCP 将数据视为一连串无结构的字节流,没有固定的“消息”边界。应用程序需要自己处理消息分包和粘包问题。 | UDP 是面向数据报的,接收到的永远是发送方发送的完整数据包。 |
| 流量控制 | 通过滑动窗口机制,接收方可以控制发送方的发送速率,防止发送过快导致接收方缓冲区溢出。 | UDP 没有流量控制,发送方会以恒定速率发送。 |
TCP: 可靠性高于UDP, 性能低于UDP
HTTP请求底层基于TCP通信
3.1 三次握手,四次握手
面试题
TCP建立连接: 3次握手

断开连接: 4次挥手

3.2 核心类TcpListenr
-
TcpListener(服务器): 底层是ServerSocket-
创建
TcpListener对象, 设置监听的端口 -
启动监听
-
等待客户端的连接
-
通过连接的
TcpClient对象得到字节流 -
通过读写进行接收与发送
-
关闭
-
-
TcpClient(客户端): 底层是Socket-
创建
TcpClient对象 -
建立连接
-
通过
TcpClient对象得到字节流(网络字节流) -
通过读写进行接收与发送
-
关闭
-
服务器代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace app1
{
internal class ServerProgram
{
static void Main(string[] args)
{
TcpListener tcpListener = null;
try
{ //1.创建TcpListener对象
tcpListener = new TcpListener(IPAddress.Loopback, 10000);
//2.启动监听
tcpListener.Start(10);
Console.WriteLine($"服务器启动成功,监听端口:{((IPEndPoint)tcpListener.LocalEndpoint).Port}");
//3.等待客户端连接
//while (true)
//{
// TcpClient tcpClient = tcpListener.AcceptTcpClient(); //阻塞方法
// //new Thread((client) => {
// // //处理客户端连接
// //}).Start();
//}
TcpClient tcpClient = tcpListener.AcceptTcpClient();//阻塞方法
//4.得到网络字节流
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}");
//发送:
byte[] sendContent = Encoding.UTF8.GetBytes("你好,客户端!");
stream.Write(sendContent,0,sendContent.Length);
stream.Flush();
Console.ReadKey();
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
}
}
客户端代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Client
{
internal class Program
{
static void Main(string[] args)
{
//客户端:
//1.创建TcpClient对象
TcpClient tcpClient = new TcpClient();
try
{
//2.连接服务器
tcpClient.Connect(IPAddress.Loopback, 10000);
//2.得到网络字节流
Stream stream = tcpClient.GetStream();
//先发后收
//发送:
byte[] sendContent = Encoding.UTF8.GetBytes("你好,服务器!");
stream.Write(sendContent, 0, sendContent.Length);
stream.Flush();
byte[] b = new byte[1024];
int len = stream.Read(b, 0, b.Length);
//输出
string content = Encoding.UTF8.GetString(b, 0, len);
Console.WriteLine($"[服务端]:{content}");
tcpClient.Close();
Console.ReadKey();
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
}
}
第三章 Linq查询语法
Linq是一个简化数组,集合查询的技术, 不掌握linq也能实现业务功能
Linq支持两种写法:
-
查询语法: 以from开头, select/group by 结尾 了解
var rs = from num in list
where num >10 && num % 2 == 0
select num;
方法语法 推荐
方法语法(也称为连贯语法)使用Enumerable 或 Queryable静态类中包含的扩展方法
集合对象/数组对象.方法1(Lambda表达式).方法2(Lambda表达式2)....
方法
| 方法 | 方法名 | 描述 |
| 排序 | OrderBy | 升序 |
| OrderByDescending | 降序 | |
| ThenBy | 次级升序 | |
| ThenByDescengding | 次级降序 | |
| 投影 | select | 映射自定义的条件属性 |
| SelectMany | 映射集合中的子集和中的元素 | |
| 连接 | Join | 4个参数(要连接的集合,自身属性,对方属性,结果投影) |
| 分组 | GroupBy | 按自定义属性分组 |
| 过滤 | Where | 条件过滤 |
| OfType<T> | 按类型过滤 |
聚合方法:
- Count() / Count(lambda) 计数
- Sum() 求和
- Max() 最大值
- Min() 最小值
- Average() 平均值
第四章 运行时常见的错误与解决办法
错误1:写代码时,经常没写捕获异常,导致程序运行容易崩溃
现象:
t1.Start(dt1);
t2.Start(dt2);
t3.Start(dt3);
t1.Join();
t2.Join();
t3.Join();
如果突发异常,程序崩溃没有处理机制
原因:
方法写了,但没处理捕获异常容易导致程序崩溃
解决方法:
用try-catch 捕获异常处理
错误2: 在处理多线程聊天室的关闭问题,关闭服务器的时候,会因为一个线程正在执行服务器的监听阻塞方法导致程序崩溃
public void Colse()
{
ServerPrint("服务器已经关闭");
listener.Stop();
ClientList.Clear();
}
原因: 此时服务器正在执行阻塞监听方法,突然中断程序崩溃
解决方法:
给方法加上异常捕获,程序崩溃时会自动中断监听方法,从而关闭方法能够正常执行
public void Colse()
{
try
{
ServerPrint("服务器已经关闭");
listener.Stop();
ClientList.Clear();
}
catch(SocketException ex)
{
Console.WriteLine($"关闭服务器异常:{ex.Message}");
}
}
错误3:编写客户端群聊提人方法时,踢出对象时只是将对象客户端关闭了,存储在集合里的对象没有删除,导致对象一直存在集合里
解决方法:
public void Remove()
{
Console.WriteLine("请输入要移除的客户端姓名:");
string name =Console.ReadLine();
if (string.IsNullOrEmpty(name))
{
Console.WriteLine("输入不能为空!");
return;
}
var targetClients = ClientList.Where(c => c.Nickname == name).ToList();
if(targetClients.Count == 0)
{
Console.WriteLine("群聊中没有该用户");
return;
}
foreach (var client in targetClients)
{
try
{
client.SocketClient?.Close();
}
catch (Exception ex)
{
Console.WriteLine($"关闭客户端Socket异常:{ex.Message}");
}
ClientList.Remove(client);
new Thread(ServerPrint).Start($"{client.Nickname}已经被踢出");
}
先前集合存在服务器对象里,其他方法无法修改集合,
1.先将局部变量的集合对象,放在类里这样其他方法都能修改
2.然后Where linq语法将筛选的对象存入一个集合,再遍历该集合中每个对象,用原集合中的Remove方法将该对象从原集合中移除,这样集合中存储的对象就移除了
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)