本周学习了线程与进程、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)时,会发生以下过程:

  1. 应用层:浏览器生成一个HTTP请求(如“GET /index.html”)。

  2. 其下各层:这个HTTP请求会被依次封装(打包)到传输层的TCP段、网络层的IP包、数据链路层的帧中,最后通过物理层的比特流发送出去。

  3. 目标服务器:服务器收到数据后,会逆向地一层层解封装,最终将HTTP请求递交给Web服务器软件(如Apache, Nginx)。

  4. 响应:Web服务器软件处理请求,生成HTTP响应(如HTML页面),再按照同样的封装过程发回给您的浏览器。

网络通信三要素

  • 协议 : 约束数据如何发送(约束数据发送的格式)

  • IP地址 : 标识数据要发送的目标设备

  • 端口 : 标识数据要发送到目标设备的那个应用

二、UDP通信协议

核心特点

  • 无连接:通信前不需要建立连接(如 TCP 的三次握手)。

  • 不可靠:不保证数据包一定能到达目的地,也不保证顺序。

  • 高效:没有连接建立和维护的开销,协议头更小,传输延迟更低。

  • 面向数据报:数据是以一个个独立的包(数据报)为单位发送和接收的。

2.1 IpAddress : ip地址类

主要属性

属性类型描述
AddressIPAddress获取或设置终结点的 IP 地址。
Portint获取或设置终结点的端口号。
AddressFamilyAddressFamily获取 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通信编码步骤:

  1. 创建UdpClient对象

  2. 调用UdpClient对象接收/发送方法

  3. 关闭资源 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方法将该对象从原集合中移除,这样集合中存储的对象就移除了

Logo

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

更多推荐