1.ui线程和后台线程
**class Program
{
static async Task Main()
{
Console.WriteLine("开始执行异步计算任务...");
int result = await PerformCalculationAsync();
Console.WriteLine($"计算结果是: {result}");
Console.WriteLine("异步计算任务执行完毕。");
}
static async Task<int> PerformCalculationAsync()
{
return await Task.Run(() =>
{
// 模拟耗时计算
int sum = 0;
for (int i = 0; i < 1000000; i++)
{
sum += i;
}
return sum;
});
}
}**
大前提:主线程是程序启动时自动创建的第一个线程,
它是程序执行的入口点。
在 C# 控制台应用程序里,Main 方法就是在主线程中执行的;
在 Windows 窗体应用程序或者 WPF 应用程序中,主线程负责处理用户界面的渲染和交互。
对以上代码解释:主线程创建后 执行main函数 看到第一个await 主线程暂停执行main 等待PerformCalculationAsync 方法返回结果 这时候
主线程可以去执行其他的任务
在 Main 方法中:await 用于暂停 Main 方法的执行,等待 PerformCalculationAsync 方法返回结果。
这确保了在获取到异步计算的结果之前,Main 方法不会继续执行输出结果等后续操作,
保证了程序逻辑的正确性和顺序性。如果没有这个 await,
Main 方法会继续执行,可能在 PerformCalculationAsync 还未完成计算时就尝试输出结果,
得到的将是不正确或未完成的结果。
在 PerformCalculationAsync 方法中:await 用于暂停 PerformCalculationAsync 方法的执行,
等待 Task.Run 启动的异步计算任务完成。它使得该方法能够异步地获取计算结果,
避免了在当前线程(这里虽然不是主线程,但如果没有 await,也会影响方法执行流程)
上阻塞等待计算完成,从而提高了代码的异步性和效率。如果没有这个 await,
方法可能会在计算任务还未完成时就继续执行后续代码(如果有后续代码的话),
导致无法正确获取计算结果。
2. _dispatcher = unityContainer.Resolve()
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using Unity;
using Zhaoxi.Controls;
using Zhaoxi.SmartParking.Client.Common;
using Zhaoxi.SmartParking.Client.Entity;
using Zhaoxi.SmartParking.Client.IBLL;
using Zhaoxi.SmartParking.Client.Models;
namespace Zhaoxi.SmartParking.Client.AutoModule.ViewModels
{
public class AutoRegisterViewModel : PageViewModelBase
{
public PaginationModel PaginationModel { get; set; } = new PaginationModel();
public ObservableCollection<AutoInfoModel> AutoList { get; set; } = new ObservableCollection<AutoInfoModel>();
Dispatcher _dispatcher;
IAutoRegisterBLL _autoRegisterBLL;
public AutoRegisterViewModel(
IRegionManager regionManager,
IUnityContainer unityContainer,
IEventAggregator ea,
IAutoRegisterBLL autoRegisterBLL
)
: base(regionManager, unityContainer, ea)
{
this.PageTitle = "车辆登记管理";
_dispatcher = unityContainer.Resolve<Dispatcher>();
_autoRegisterBLL = autoRegisterBLL;
// 中间的页码点击
// 向前向后 也会触发
PaginationModel.NavCommand = new DelegateCommand<object>(obj =>
{
Index = int.Parse(obj.ToString());
this.Refresh();
});
// 改变每页条目数
PaginationModel.CountPerPageChangeCommand = new DelegateCommand(() =>
{
this.Refresh();
});
}
private int Index = 1;
public override void Refresh()
{
this.ShowLoading();
AutoList.Clear();
///回调 Thread C#
Task.Run(async () =>
{
var result =await _autoRegisterBLL.GetAll(Index, PaginationModel.CountPerPage);
if (result.State == 1)
{
var datas = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AutoRegisterEntity>>(result.Data);
_dispatcher.Invoke(() =>
{
// 填充车辆信息
for (int i = 0; i < datas.Count; i++)
{
AutoList.Add(new AutoInfoModel
{
Num = (Index - 1) * PaginationModel.CountPerPage + i + 1,
AutoLicense = datas[i].AutoLicense,
LColorId = datas[i].LColorId,
LColorName = datas[i].LColorName,
AColorId = datas[i].AColorId,
AColorName = datas[i].AColorName,
FeeModelId = datas[i].FeeModelId,
FeeModelName = datas[i].FeeModelName,
Description = datas[i].Description
});
}
// 填充页码
PaginationModel.FillPages(result.PageInfo.RecordCount, result.PageInfo.PageIndex);
});
}
this.HideLoading();
});
}
public override void AddItem()
{
base.AddItem();
}
}
}
_dispatcher = unityContainer.Resolve(); 含义
依赖注入容器:unityContainer 是使用 Unity(一种依赖注入容器 )创建的对象。
依赖注入是一种软件设计模式,通过将对象所依赖的其他对象传递给它,而不是让对象自己创建依赖项,
从而实现松耦合。
Resolve 方法:Resolve 是 Unity 容器的方法 ,作用是从容器中获取指定类型的实例。 表示期望获取的对象类型为 Dispatcher 。
在这里,_dispatcher = unityContainer.Resolve();
就是从 unityContainer 中获取一个 Dispatcher 实例,并将其赋值给 _dispatcher 字段 。
Dispatcher 常见于 Windows Presentation Foundation(WPF)等 UI 框架,是用于协调和管理线程工作的重要组件,主要作用如下:
确保 UI 线程安全更新
在 WPF 应用程序中,UI 元素只能在创建它们的线程(即 UI 线程 )上进行修改。当进行如异步操作(后台任务、网络请求 )、定时器操作等时,这些操作可能在非 UI 线程上执行。Dispatcher 提供了一种机制,允许在非 UI 线程上执行的代码能够安全地在 UI 线程上更新 UI 元素 。比如从后台线程获取数据后,需更新 UI 显示,就要通过 Dispatcher 来执行更新操作,防止跨线程访问 UI 引发的线程安全问题。
在这里注册了注册 Dispatcher
/// 全局对象注册
containerRegistry.Register(() => Application.Current.Dispatcher);
注意:由于 Prism 与 Unity 容器的集成以及依赖注入容器的类型注册和解析机制,_dispatcher = unityContainer.Resolve(); 能够取到通过 containerRegistry.Register(() => Application.Current.Dispatcher); 注册的 Dispatcher 实例。
Application.Current 用于获取当前应用程序实例,Application.Current.Dispatcher 则获取当前应用程序的 Dispatcher 实例。这行代码的作用就是将当前应用程序的 Dispatcher 实例注册到容器中,后续就可以从容器中解析获取该实例,用于在不同地方安全地进行 UI 线程相关操作 。
3. IRegionManager regionManager, IUnityContainer unityContainer, IEventAggregator ea,IAutoRegisterBLL autoRegisterBLL
3.1 IRegionManager regionManager:
IRegionManager 是 Prism 框架中的一个接口,用于管理应用程序中的区域(Region)。区域是一种在 WPF 应用中用于动态显示内容的容器机制。
**regionManager 实例可以用来注册区域、导航到不同的视图并将视图显示在指定的区域中。**通过它,开发者可以实现模块化的 UI 设计,不同的模块可以向特定区域提供和显示自己的视图,而无需紧密耦合。例如,可以在主窗口中定义多个区域,然后使用 regionManager 将不同功能模块的视图加载并显示到相应的区域中。
3.2 IUnityContainer unityContainer:
IUnityContainer 是 Unity 依赖注入容器的接口。依赖注入是一种软件设计模式,它允许将对象的依赖关系通过外部传递进来,而不是让对象自己创建依赖项。
unityContainer 实例提供了一系列方法,用于注册类型(将抽象类型与具体实现类型关联起来)、解析类型(从容器中获取已注册类型的实例)等操作。在应用程序中,通过 unityContainer 可以方便地管理对象的生命周期,实现依赖关系的解耦,提高代码的可维护性和可测试性。例如,可以将业务逻辑层的服务类注册到容器中,然后在视图模型中通过容器解析获取这些服务的实例来使用。
3.3 IEventAggregator ea:
IEventAggregator 是 Prism 框架中的一个接口,用于实现事件聚合模式。事件聚合模式提供了一种在不同模块、视图模型或其他组件之间进行通信的机制。
ea 实例允许发布者(Publisher)发布特定类型的事件,而订阅者(Subscriber)可以订阅这些事件并在事件发生时执行相应的处理逻辑。通过事件聚合器,不同组件之间可以实现松耦合的通信,无需直接引用对方。例如,一个模块中的视图模型可以发布一个 “数据更新完成” 的事件,其他模块中的视图模型如果对这个事件感兴趣,可以订阅该事件并在事件发生时更新自己的 UI 显示。
4.socket
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Zhaoxi.SocketServer
{
class Program
{
static void Main(string[] args)
{
Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketServer.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001));//设置对应的主机地址(IP、Port)
socketServer.Listen(10);// 监听 表示等待连接对象只能有10个
Console.WriteLine("服务启动。。。");
var client = socketServer.Accept();
Console.WriteLine("有客户端连接了。。。");
Console.ReadLine();
}
}
}
Accept 方法是阻塞方法,用于在监听的端口上接受传入的客户端连接请求,接收到连接后返回代表该客户端连接的 Socket 对象,这是服务端用于与客户端建立通信链路的关键操作。
这是服务端 运行到accept方法时候阻塞然后等待客户端连接 后面代码暂时不执行
Listen 方法开始监听客户端连接请求,说明它在等待客户端连接
4.1
服务端代码
using System;
using System.Net;
using System.Net.Sockets;
namespace Zhaoxi.SocketServer
{
class Program
{
static void Main(string[] args)
{
Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketServer.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001));
socketServer.Listen(10);
Console.WriteLine("服务启动。。。");
var client = socketServer.Accept();
Console.WriteLine("有客户端连接了。。。");
Console.ReadLine();
}
}
}
客户端代码
using System;
using System.Net;
using System.Net.Sockets;
namespace Zhaoxi.SocketClient
{
class Program
{
static void Main(string[] args)
{
try
{
Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketClient.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001));
Console.WriteLine("连接成功");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
}
这里用了一个监听listen(1)表示等待的对象只能有一个,这个是单线程,看图是连接两的第三个就会连接失败,虽然是连接两个,实际上有一个是等待状态不能进行通讯。其实都可以通讯
单线程
static void Main(string[] args)
{
Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketServer.Bind(new IPEndPoint(IPAddress.Parse(“127.0.0.1”), 9001));//设置对应的主机地址(IP、Port)
socketServer.Listen(1);// 监听 表示等待连接对象只能有1个
Console.WriteLine("服务启动。。。。");
// await Task.Delay(200);
var client = socketServer.Accept();
Console.WriteLine("有客户端连接了111111");
// 为每个客户端创建独立线程处理通信
//new Thread(() => HandleClient(client)).Start();
//client.Receive();
//client.Send();
Console.WriteLine("主线程1");
Console.ReadLine();
Console.WriteLine("主线程2");
}
这里连到第三个客户端会失败 因为lisent(1)规定队列里面有一个连接等待 第一个连接被accept调用 第二个等待 第三个失败 这时候加上循环当有连接的时候不断调用 accept 就不会连接失败
单线程加循环
static void Main(string[] args)
{
Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketServer.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001));//设置对应的主机地址(IP、Port)
socketServer.Listen(1);// 监听 表示等待连接对象只能有1个
Console.WriteLine("服务启动。。。。");
while (true)
{
// await Task.Delay(200);
var client = socketServer.Accept();
Console.WriteLine("有客户端连接了111111");
// 为每个客户端创建独立线程处理通信
//new Thread(() => HandleClient(client)).Start();
//client.Receive();
//client.Send();
}
Console.WriteLine("主线程1");
Console.ReadLine();
Console.WriteLine("主线程2");
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketServer.Bind(new IPEndPoint(IPAddress.Parse(“127.0.0.1”), 9001));
socketServer.Listen(1); // 监听,表示等待连接对象只能有1个(此处注释说明有误,1是等待连接队列长度)
Console.WriteLine(“服务启动。。。”);
Task.Run(async () =>
{
while (true)
{
await Task.Delay(200);
var client = socketServer.Accept();
client.Receive(buffer)
Console.WriteLine("有客户端连接了。。。");
}
});
Console.ReadLine();
}
}
}
单线程阻塞:这段代码确实没有显式创建新线程,所有操作都在主线程中完成。
问题所在:client.Receive(buffer) 是同步阻塞调用。如果当前客户端不发送数据,Receive 会一直阻塞主线程,导致服务器无法调用 Accept() 接受新客户端连接。
结果:服务器同一时间只能处理一个客户端,其他客户端必须等待当前客户端完成数据传输。
多线程
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketServer.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001));
socketServer.Listen(1); // 监听,表示等待连接对象只能有1个(此处注释说明有误,1是等待连接队列长度)
Console.WriteLine("服务启动。。。");
Task.Run(async () =>
{
while (true)
{
await Task.Delay(200);
var client = socketServer.Accept();
Console.WriteLine("有客户端连接了。。。");
}
});
Console.ReadLine();
}
}
}
这个好处就是不阻塞主线程
真正异步阻塞
Task.Run(async () =>
{
while (true)
{
//注意:accept()是同步方法不能用await 所以改成 acceptAsync
var client = await socketServer.AcceptAsync(); // 异步非阻塞!
Console.WriteLine("有客户端连接了。。。");
// 异步处理客户端(不阻塞接受新连接的循环)
_ = HandleClientAsync(client);
}
});
static async Task HandleClientAsync(Socket client)
{
try
{
byte[] buffer = new byte[1024];
// 异步接收数据
await client.ReceiveAsync(buffer, SocketFlags.None);
// 处理数据...
}
finally
{
client.Dispose();
}
}
注意:accept()是同步方法不能用await 所以改成 acceptAsync
遇到 await 时:
当前线程被释放:执行 AcceptAsync() 的线程(假设为线程 T)会立即返回线程池,不再被阻塞。
注册回调:系统会在 AcceptAsync() 完成(即有新连接到达)时,自动触发后续代码的执行。
异步操作完成后:
调度恢复执行:当新连接到达时,线程池会调度另一个线程(或复用线程 T)来执行 await 之后的代码(如 Console.WriteLine)。
延续执行:后续代码(如处理客户端逻辑)会在这个新线程上继续执行。
时间线:
[步骤1] 线程T开始执行循环 -> 调用 AcceptAsync()
[步骤2] AcceptAsync() 返回一个未完成的 Task -> 线程T被释放回线程池
[步骤3] 主线程继续执行其他任务(或空闲)
[步骤4] 新客户端连接到达 -> AcceptAsync() 完成
[步骤5] 线程池调度线程S(可能是T或其他线程)执行:
Console.WriteLine(“有客户端连接了。。。”);
// 继续执行后续代码