弱引用(Weak Reference):极简工业上位机视角解读
一、先搞懂「强引用」(对比才能懂弱引用)
强引用 = 用绳子拴住设备
比如你代码里写:
csharp
var vm = new VisionViewModel(); // 强引用:vm变量像绳子拴住ViewModel实例
只要这根"绳子"还在(变量没置空、没出作用域),GC(垃圾回收器)就绝对不会回收这个ViewModel------哪怕界面已经关闭,实例依然占着内存,就像工人下班了但还被绳子拴在工位上,导致内存泄漏。
二、弱引用(WeakReferenceMessenger的核心)
弱引用 = 用粉笔在设备上做标记
WeakReferenceMessenger注册消息时,对接收者(比如你的VM)只做"粉笔标记",不拴绳子:
- 当VM该被回收时(比如窗口关闭、Dispose调用),GC能直接清理VM,不受Messenger影响
- 标记会自动消失,不会像强引用那样"拴住"实例导致内存泄漏
三、为什么工业上位机必须用WeakReferenceMessenger?
| 场景 | 强引用Messenger | WeakReferenceMessenger(推荐) |
|---|---|---|
| 内存泄漏 | 极易发生:注册后忘记Unregister,VM永远无法回收 | 几乎不会:GC自动清理无引用的VM |
| 设备长时间运行 | 内存越用越多,最终程序崩溃 | 内存稳定,适合7×24小时工控场景 |
| 多VM解耦 | 解耦但留内存隐患 | 彻底解耦+内存安全 |
四、代码级对比(一眼看懂)
❌ 强引用(危险,工控禁用)
csharp#
### 一、先搞懂「强引用」(对比才能懂弱引用)
**强引用 = 用绳子拴住设备**
比如你代码里写:
```csharp
var vm = new VisionViewModel(); // 强引用:vm变量像绳子拴住ViewModel实例
只要这根"绳子"还在(变量没置空、没出作用域),GC(垃圾回收器)就绝对不会回收这个ViewModel------哪怕界面已经关闭,实例依然占着内存,就像工人下班了但还被绳子拴在工位上,导致内存泄漏。
二、弱引用(WeakReferenceMessenger的核心)
弱引用 = 用粉笔在设备上做标记
WeakReferenceMessenger注册消息时,对接收者(比如你的VM)只做"粉笔标记",不拴绳子:
- 当VM该被回收时(比如窗口关闭、Dispose调用),GC能直接清理VM,不受Messenger影响
- 标记会自动消失,不会像强引用那样"拴住"实例导致内存泄漏
三、为什么工业上位机必须用WeakReferenceMessenger?
| 场景 | 强引用Messenger | WeakReferenceMessenger(推荐) |
|---|---|---|
| 内存泄漏 | 极易发生:注册后忘记Unregister,VM永远无法回收 | 几乎不会:GC自动清理无引用的VM |
| 设备长时间运行 | 内存越用越多,最终程序崩溃 | 内存稳定,适合7×24小时工控场景 |
| 多VM解耦 | 解耦但留内存隐患 | 彻底解耦+内存安全 |
四、代码级对比(一眼看懂)
❌ 强引用(危险,工控禁用)
csharp
// 强引用注册:Messenger死死拴住this(VM),哪怕Dispose也可能漏解注册
Messenger.Default.Register<ScanMessage>(this, (msg) => { /*处理逻辑*/ });
// 忘记Unregister → VM永远占内存 → 设备运行1天就卡顿
✅ 弱引用(安全,工控必用)
csharp
// 弱引用注册:Messenger只标记this,不拴住
WeakReferenceMessenger.Default.Register<ScanMessage>(this, (r, msg) => { /*处理逻辑*/ });
// 即使忘记Unregister,GC也能回收VM,内存不泄漏
五、弱引用的关键特性(工控开发必记)
- 不影响GC回收:弱引用不会阻止GC清理目标对象,这是和强引用的核心区别
- 自动失效:目标对象被回收后,弱引用会自动变成"空",不会指向无效内存(避免野指针)
- 需配合Dispose :虽然弱引用更安全,但工控场景仍建议在Dispose中调用
UnregisterAll------双重保险,避免极端场景下的泄漏
六、工业上位机实战注意点
-
WeakReferenceMessenger不是"免死金牌":
- 如果VM自身有强引用(比如静态变量持有、Timer事件没解绑),弱引用也救不了,依然会泄漏
- 必须配合Dispose:Cancel CTS + Stop Timer + Dispose Halcon + UnregisterAll
-
调试弱引用泄漏 :
用Visual Studio"内存诊断工具":
- 捕获内存快照 → 搜索VM类名 → 查看"引用根"
- 如果根是
WeakReferenceMessenger,说明没UnregisterAll;如果是Timer/CTS,说明没释放资源
总结
- 弱引用 = GC友好的"临时标记",强引用 = "永久绑定"
- WeakReferenceMessenger用弱引用管理消息订阅,是工控软件长时间运行不泄漏的核心保障
- 工控开发中:必须用WeakReferenceMessenger + 完整Dispose,二者缺一不可
// 强引用注册:Messenger死死拴住this(VM),哪怕Dispose也可能漏解注册
Messenger.Default.Register(this, (msg) => { /处理逻辑 / });
// 忘记Unregister → VM永远占内存 → 设备运行1天就卡顿
```csharp
// 弱引用注册:Messenger只标记this,不拴住
WeakReferenceMessenger.Default.Register<ScanMessage>(this, (r, msg) => { /*处理逻辑*/ });
// 即使忘记Unregister,GC也能回收VM,内存不泄漏
五、弱引用的关键特性(工控开发必记)
- 不影响GC回收:弱引用不会阻止GC清理目标对象,这是和强引用的核心区别
- 自动失效:目标对象被回收后,弱引用会自动变成"空",不会指向无效内存(避免野指针)
- 需配合Dispose :虽然弱引用更安全,但工控场景仍建议在Dispose中调用
UnregisterAll------双重保险,避免极端场景下的泄漏
六、工业上位机实战注意点
-
WeakReferenceMessenger不是"免死金牌":
- 如果VM自身有强引用(比如静态变量持有、Timer事件没解绑),弱引用也救不了,依然会泄漏
- 必须配合Dispose:Cancel CTS + Stop Timer + Dispose Halcon + UnregisterAll
-
调试弱引用泄漏 :
用Visual Studio"内存诊断工具":
- 捕获内存快照 → 搜索VM类名 → 查看"引用根"
- 如果根是
WeakReferenceMessenger,说明没UnregisterAll;如果是Timer/CTS,说明没释放资源
总结
- 弱引用 = GC友好的"临时标记",强引用 = "永久绑定"
- WeakReferenceMessenger用弱引用管理消息订阅,是工控软件长时间运行不泄漏的核心保障
- 工控开发中:必须用WeakReferenceMessenger + 完整Dispose,二者缺一不可
核心结论(一句话记死):
this就是当前这个VM本身 (比如你的AxisEcatVM),它通过WeakReferenceMessenger.Default(唯一中转站)"登记订阅"ScanMessage------本质是VM告诉中转站:"有ScanMessage消息时喊我,我来执行处理逻辑",中转站只做"传话筒",最终干活的还是这个VM。
用「工厂调度」再强化类比
假设你是轴控车间的班组长(当前VM,this) ,WeakReferenceMessenger.Default是工厂唯一的调度室(中转站单例):
- 订阅 = 你去调度室登记 :
你走到调度室说:"凡是'启动扫描(ScanMessage)'的指令,都通知我,我来安排轴运动(处理逻辑)"。
→ 对应代码:WeakReferenceMessenger.Default.Register<ScanMessage>(this, (r, msg) => { 轴运动逻辑 }); - 调度室的角色 :
调度室只记下来"启动扫描指令→找轴控班组长",它自己不会操作轴、不会跑逻辑,只是个"指令转发器"; - 发消息 = 厂长下指令 :
厂长(UI层/视觉VM)给调度室说"发启动扫描指令",调度室翻登记本找到你,把指令传给你; - 执行功能 = 你安排干活 :
你(轴控VM)收到指令后,自己安排轴运动(调用StartScanInternal)------最终执行功能的是订阅的VM,不是中转站。
代码层面的"谁在干活"(逐句标清楚):
csharp
WeakReferenceMessenger.Default.Register<ScanMessage>(
this, // 干活的人:当前VM(AxisEcatVM)
(r, msg) => {
// 干活的内容:VM自己执行扫描逻辑
StartScanInternal(msg.TargetX, msg.TargetY);
});
WeakReferenceMessenger.Default:只负责"记下来谁要干这个活"+"收到指令后喊人";this:是真正"接活、干活"的主体;- 箭头函数里的逻辑:是VM要干的具体活。
为什么要绕这一层(工控场景的核心价值):
如果没有中转站,UI按钮要直接调用AxisEcatVM的StartScanInternal------意味着UI层必须"认识"AxisEcatVM(引用它),一旦你换了轴控模块(比如从EtherCAT换成Modbus),UI层代码也要改;
有了中转站后:
- UI层只需要"给调度室发指令",不用管谁来干活;
- 轴控VM只需要"在调度室登记接活",不用管谁发指令;
- 后续换轴控模块,只需要新模块去调度室登记"接启动扫描的活",UI层一行代码都不用改------这就是工控软件需要的"解耦"。
最终简化记忆:
- 中转站(Default)= 调度室(只传指令,不干活);
- 订阅 = 班组长(VM,this)去调度室登记"接某类活";
- 执行 = 班组长收到指令后,自己安排干活。
你的理解已经完全到位了,这个逻辑是MVVM通信的核心,也是工控软件模块化开发的关键~
r和msg 极简解读(工控场景版)
在这段注册代码里,r 和 msg 是lambda表达式的两个参数,对应"订阅消息后收到通知时的输入数据",用工厂调度的类比能一眼懂:
csharp
WeakReferenceMessenger.Default.Register<ScanMessage>(
this,
(r, msg) => { /*处理逻辑*/ } // 重点看这两个参数
);
1. msg(核心参数,必须用)
-
含义 :
msg= 你收到的「具体指令内容」(就是发送方发来的ScanMessage对象) -
类比:调度室转给你的"启动扫描指令单",上面写着目标X/Y坐标、曝光时间等具体参数
-
实际用途 :在处理逻辑里取指令参数,比如:
csharp(r, msg) => { // 从msg里拿指令参数,执行扫描 StartScanInternal(msg.TargetX, msg.TargetY, msg.ExposureMs); } -
关键 :
msg的类型就是你注册时<>里的ScanMessage,只能拿到这个类型的消息数据。
2. r(可选参数,几乎不用)
-
含义 :
r= 订阅消息的"接收者本身"(就是你写的this,当前VM实例) -
类比:调度室在给你指令单时,顺便告诉你"这份指令是给XX班组长(你自己)的"
-
实际用途 :99%的工控场景用不到,因为
r和this是同一个对象,比如:csharp(r, msg) => { // r == this(当前AxisEcatVM实例),完全等价 r.StartScanInternal(msg.TargetX, msg.TargetY); this.StartScanInternal(msg.TargetX, msg.TargetY); } -
简化写法 :既然用不到
r,可以直接省略,写成:csharpWeakReferenceMessenger.Default.Register<ScanMessage>( this, (_, msg) => { // 下划线表示"这个参数不用,只是占位" StartScanInternal(msg.TargetX, msg.TargetY); } );
核心总结
| 参数 | 本质 | 用途 |
|---|---|---|
| msg | 发来的ScanMessage消息对象 | 取指令参数(X/Y坐标、曝光时间等),核心必用 |
| r | 订阅消息的VM本身(this) | 几乎不用,可下划线占位省略 |
简单说:=只需要关注msg------它是发送方传的"指令详情",r只是"这是给你的指令"。
OperationCanceledException 核心理解
1. 一句话定义
操作被取消异常 → 这不是程序报错!不是BUG!
是你调用 _cts.Cancel()(急停/停止任务)时,.NET 专门抛出的正常终止信号,意思是:
后台任务(轴扫描、图像采集)主动被你取消了,不是崩溃、不是故障,是正常停止!
2. 对应你的工控场景
后台任务:
- 任务 = 轴持续扫描 / Halcon图像采集
_cts.Cancel()= 按下急停按钮 / 点击停止扫描OperationCanceledException= 任务收到急停信号,优雅停下时发出的通知
3. 什么时候会触发?
只要你用了 CancellationToken,这3个地方会自动抛这个异常:
token.ThrowIfCancellationRequested();await Task.Delay(200, token);- 异步任务中检测到取消信号时
4. 为什么要捕获它?(关键!)
如果不捕获:
- 正常的停止任务会被程序当成「崩溃报错」,弹出错误、记录异常日志;
- 你明明是手动停止扫描,结果程序红屏报警,不符合工控逻辑。
正确逻辑 :
捕获这个异常 → 标记任务正常取消 → 不报错、只记录日志。
写法
csharp
try
{
// 你的后台扫描任务(无限循环读取轴坐标)
while (!token.IsCancellationRequested)
{
// 读取轴位置、刷新UI
await ReadAxisStatusAsync(token);
await Task.Delay(200, token);
}
}
// 专门捕获:任务被取消(正常停止)
catch (OperationCanceledException ex)
{
// 工控逻辑:这里只写日志,不报错!
Console.WriteLine("轴扫描任务 → 已正常停止(手动取消)");
}
// 捕获真正的错误(比如轴通信失败)
catch (Exception ex)
{
// 真正的故障:报警、弹窗
Console.WriteLine($"轴扫描故障:{ex.Message}");
}
| 代码 | 动作 | 结果 |
|---|---|---|
_cts = new CTS() |
创建取消令牌 | 准备控制任务 |
_cts.Cancel() |
发送停止信号(急停) | 触发取消 |
OperationCanceledException |
任务响应停止 | 正常终止,不是报错 |
UnregisterAll |
注销消息 | 彻底清理 |
最终总结
OperationCanceledException= 任务被手动取消的正常信号(急停、停止扫描);- 不是异常故障,千万不要当成程序错误;
- 捕获它 = 区分「我主动停的 」和「程序坏了」;
- 是你工控上位机急停功能的标准配套异常。
bash
WpfApp6/MainWindow.xaml:26: <TextBlock Text="{Binding MainVM.
Status}" FontSize="16" FontWeight="Bold" Foreground="blue"/>
WpfApp6/MainWindow.xaml:30: <TextBlock Grid.Row="0" Grid.Column="
1" Text="{Binding MainVM.BusStatus}"
WpfApp6/MainWindow.xaml:46: Text="{Binding
MainVM.ExposureMs,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:47: IsEnabled="{Bind
ing MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:50: Text="{Binding M
ainVM.ScanSpeed,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:51: IsEnabled="{Bind
ing MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:54: :...skipping...
WpfApp6/MainWindow.xaml:26: <TextBlock Text="{Binding MainVM.Status}" FontSize="16" FontWeight="Bold" Foreground="blue"/>
WpfApp6/MainWindow.xaml:30: <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding MainVM.BusStatus}"
WpfApp6/MainWindow.xaml:46: Text="{Binding MainVM.ExposureMs,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:47: IsEnabled="{Binding MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:50: Text="{Binding MainVM.ScanSpeed,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:51: IsEnabled="{Binding MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:54: Text="{Binding MainVM.TargetX,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:55: IsEnabled="{Bind
ing MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:60: Value="{Binding MainVM.R
ate}" Minimum="0" Maximum="1"
WpfApp6/MainWindow.xaml:63: ItemsSource="{Binding MainVM
.LogList}" Height="auto" Margin="10">
WpfApp6/MainWindow.xaml:78: <Button Command="{Binding MainVM.
StartScanCommand}" Content="点位扫描"
WpfApp6/MainWindow.xaml:80: <Button Command="{Binding MainVM.
StartScanCommand}" Content="连续扫描"
WpfApp6/MainWindow.xaml:82: <Button Command="{Binding MainVM.
StopScanCommand}" Content="停止"
WpfApp6/MainWindow.xaml:28: <TextBlock Text="{Binding AxisVM.
AxisStatusText}" FontSize="14" Foreground="DarkGreen"/>
Archive/legacy/MainViewModel.cs:13: public class MainViewModel:ViewMo
delBase
Archive/legacy/MainViewModel.cs:18: public MainViewModel(IXYScanA
xis scanAxis, IEtherCATMaster ecatMaster, ILogService logService)
WpfApp6/MainWindow.xaml.cs:28: // private readonly MainViewModel_viewModel;
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git grep -n "WeakReferenceMessenger.Default.Register<" -- "*.cs" || tr
ue
git grep -n "WeakReferenceMessenger.Default.Send" -- "*.cs" || true
WpfApp6/ViewModel/AxisEcatVM.cs:57: WeakReferenceMessenger.De
fault.Register<StartScanMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/AxisEcatVM.cs:63: WeakReferenceMessenger.Default.Register<StopScanMessage>(this, (recipient, msg) => Stop());
WpfApp6/ViewModel/AxisEcatVM.cs:64: WeakReferenceMessenger.De
fault.Register<UpdateTargetMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:101: WeakReferenceMessenger.
Default.Register<LogMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:105: WeakReferenceMessenger.
Default.Register<BusStatusMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:109: WeakReferenceMessenger.
Default.Register<ScanProgressMessage>(this, (recipient, msg) => {
WpfApp6/ViewModel/XrayImageVM.cs:25: WeakReferenceMessenger.D
efault.Register<AxisPositionReadyMessage>(this, (r, m) =>
WpfApp6/ViewModel/XrayImageVM.cs:29: WeakReferenceMessenger.Default.Register<StopScanMessage>(this, (r, m) =>
WpfApp6/Services/LogService.cs:32: CommunityT
oolkit.Mvvm.Messaging.WeakReferenceMessenger.Default.Send(new Messages.AppMessages.LogMessage($"LogService" +
WpfApp6/Services/LogService.cs:42: CommunityToolkit.Mvvm.Messaging.WeakReferenceMessenger.Default.Send(new Messages.AppMessages.LogMessage($"LogService" +
WpfApp6/ViewModel/AxisEcatVM.cs:59: WeakReferenceMessenger.Default.Send(new LogMessage($"[DEBUG] AxisEcatVM收到 StartScanMessage:NewX={msg.TargetX}" +
WpfApp6/ViewModel/AxisEcatVM.cs:66: WeakReferenceMessenge
r.Default.Send(new LogMessage($"[DEBUG] AxisEcatVM收到 UpdateTarget:NewX={msg.NewTargetX}"));
WpfApp6/ViewModel/AxisEcatVM.cs:81: WeakReferenceMessenger.Default.Send(new LogMessage("已有扫描任务运行,忽略新的请求"));
WpfApp6/ViewModel/AxisEcatVM.cs:102: WeakReferenceMessenger.Default.Send(new LogMessage($"扫描任务异常:{ex?.Message}"));
WpfApp6/ViewModel/AxisEcatVM.cs:116: WeakReferenceMessenger.Default.Send(new BusStatusMessage(busLog));
WpfApp6/ViewModel/AxisEcatVM.cs:119: WeakReferenceMes
senger.Default.Send(new ScanProgressMessage(progress));
WpfApp6/ViewModel/AxisEcatVM.cs:122: WeakReferenceMessenger.Default.Send(new LogMessage($"[限位报警]X坐标超出阈值({_scanAxis.Posx:F1})"));
WpfApp6/ViewModel/AxisEcatVM.cs:142: WeakReferenceMessenger.Default.Send(new LogMessage($"轴移动至 X:{targetX:f1} Y:{targetY:F1}"
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$

csharp
WpfApp6/MainWindow.xaml:26: <TextBlock Text="{Binding MainVM.
Status}" FontSize="16" FontWeight="Bold" Foreground="blue"/>
WpfApp6/MainWindow.xaml:30: <TextBlock Grid.Row="0" Grid.Column="
1" Text="{Binding MainVM.BusStatus}"
WpfApp6/MainWindow.xaml:46: Text="{Binding
MainVM.ExposureMs,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:47: IsEnabled="{Bind
ing MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:50: Text="{Binding M
ainVM.ScanSpeed,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:51: IsEnabled="{Bind
ing MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:54: :...skipping...
WpfApp6/MainWindow.xaml:26: <TextBlock Text="{Binding MainVM.Status}" FontSize="16" FontWeight="Bold" Foreground="blue"/>
WpfApp6/MainWindow.xaml:30: <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding MainVM.BusStatus}"
WpfApp6/MainWindow.xaml:46: Text="{Binding MainVM.ExposureMs,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:47: IsEnabled="{Binding MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:50: Text="{Binding MainVM.ScanSpeed,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:51: IsEnabled="{Binding MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:54: Text="{Binding MainVM.TargetX,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"
WpfApp6/MainWindow.xaml:55: IsEnabled="{Bind
ing MainVM.CanEditParam}"/>
WpfApp6/MainWindow.xaml:60: Value="{Binding MainVM.R
ate}" Minimum="0" Maximum="1"
WpfApp6/MainWindow.xaml:63: ItemsSource="{Binding MainVM
.LogList}" Height="auto" Margin="10">
WpfApp6/MainWindow.xaml:78: <Button Command="{Binding MainVM.
StartScanCommand}" Content="点位扫描"
WpfApp6/MainWindow.xaml:80: <Button Command="{Binding MainVM.
StartScanCommand}" Content="连续扫描"
WpfApp6/MainWindow.xaml:82: <Button Command="{Binding MainVM.
StopScanCommand}" Content="停止"
WpfApp6/MainWindow.xaml:28: <TextBlock Text="{Binding AxisVM.
AxisStatusText}" FontSize="14" Foreground="DarkGreen"/>
Archive/legacy/MainViewModel.cs:13: public class MainViewModel:ViewMo
delBase
Archive/legacy/MainViewModel.cs:18: public MainViewModel(IXYScanA
xis scanAxis, IEtherCATMaster ecatMaster, ILogService logService)
WpfApp6/MainWindow.xaml.cs:28: // private readonly MainViewModel _viewModel;
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git grep -n "WeakReferenceMessenger.Default.Register<" -- "*.cs" || tr
ue
git grep -n "WeakReferenceMessenger.Default.Send" -- "*.cs" || true
WpfApp6/ViewModel/AxisEcatVM.cs:57: WeakReferenceMessenger.De
fault.Register<StartScanMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/AxisEcatVM.cs:63: WeakReferenceMessenger.Default.Register<StopScanMessage>(this, (recipient, msg) => Stop());
WpfApp6/ViewModel/AxisEcatVM.cs:64: WeakReferenceMessenger.De
fault.Register<UpdateTargetMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:101: WeakReferenceMessenger.
Default.Register<LogMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:105: WeakReferenceMessenger.
Default.Register<BusStatusMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:109: WeakReferenceMessenger.
Default.Register<ScanProgressMessage>(this, (recipient, msg) => {
WpfApp6/ViewModel/XrayImageVM.cs:25: WeakReferenceMessenger.D
efault.Register<AxisPositionReadyMessage>(this, (r, m) =>
WpfApp6/ViewModel/XrayImageVM.cs:29: WeakReferenceMessenger.Default.Register<StopScanMessage>(this, (r, m) =>
WpfApp6/Services/LogService.cs:32: CommunityT
oolkit.Mvvm.Messaging.WeakReferenceMessenger.Default.Send(new Messages.AppMessages.LogMessage($"LogService" +
WpfApp6/Services/LogService.cs:42: CommunityToolkit.Mvvm.Messaging.WeakReferenceMessenger.Default.Send(new Messages.AppMessages.LogMessage($"LogService" +
WpfApp6/ViewModel/AxisEcatVM.cs:59: WeakReferenceMessenger.Default.Send(new LogMessage($"[DEBUG] AxisEcatVM收到 StartScanMessage:NewX={msg.TargetX}" +
WpfApp6/ViewModel/AxisEcatVM.cs:66: WeakReferenceMessenge
r.Default.Send(new LogMessage($"[DEBUG] AxisEcatVM收到 UpdateTarget:NewX={msg.NewTargetX}"));
WpfApp6/ViewModel/AxisEcatVM.cs:81: WeakReferenceMessenger.Default.Send(new LogMessage("已有扫描任务运行,忽略新的请求"))
;
WpfApp6/ViewModel/AxisEcatVM.cs:102: WeakReferenceMes
senger.Default.Send(new LogMessage($"扫描任务异常:{ex?.Message}"));
WpfApp6/ViewModel/AxisEcatVM.cs:116: WeakReferenceMes
senger.Default.Send(new BusStatusMessage(busLog));
WpfApp6/ViewModel/AxisEcatVM.cs:119: WeakReferenceMes
senger.Default.Send(new ScanProgressMessage(progress));
WpfApp6/ViewModel/AxisEcatVM.cs:122: WeakReferenceMessenger.Default.Send(new LogMessage($"[限位报警]X坐标超出阈值({_scanA
xis.Posx:F1})"));
WpfApp6/ViewModel/AxisEcatVM.cs:142: WeakReferenceMesseng
er.Default.Send(new LogMessage($"轴移动至 X:{targetX:f1} Y:{targetY:F1}"
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git grep -n "new CancellationTokenSource" -- "*.cs" || true
git grep -n -e "\.Wait(" -- "*.cs" || true
git grep -n -e "\.Result" -- "*.cs" || true
git grep -n "Dispatcher.Invoke(" -- "*.cs" || true
git grep -n "Dispatcher.InvokeAsync(" -- "*.cs" || true
git grep -n "async void" -- "*.cs" || true
git grep -n "WeakReferenceMessenger.Default.Register<" -- "*.cs" || true
git grep -n "UnregisterAll(this)" -- "*.cs" || true
git grep -n "Task.Run(" -- "*.cs" || true
git grep -n "Task.Delay(" -- "*.cs" || true
Archive/Legacy/AxisControlViewModel.cs:64: _cts = new Cancell
ationTokenSource();
WpfApp6/ViewModel/AxisEcatVM.cs:93: _cts = new Cancellati
onTokenSource();
WpfApp6/ViewModel/AxisEcatVM.cs:136: //_cts = new Cancell
ationTokenSource();
WpfApp6/EtherCATMaster.cs:41: //Task.Delay(1000).Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:88: // wait.Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:207: // Task.Delay(100).Wait(); // 等待任务响应取消
WpfApp6/ViewModel/AxisEcatVM.cs:230: //wait.Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:292: wait.Wait();
WpfApp6/EtherCATMaster.cs:28: Application.Current.Dispatcher.InvokeAsync(() => BusLog = "EtherCAT总线初始化中...");
WpfApp6/EtherCATMaster.cs:30: Application.Current.Dispatcher.InvokeAsync(() =>
WpfApp6/EtherCATMaster.cs:39: Application.Current.Dispatcher.InvokeAsync(() => BusLog = $"总线初始化失败:{ex.Message}");
WpfApp6/ViewModel/AxisEcatVM.cs:111: System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
WpfApp6/ViewModel/XrayImageVM.cs:41: Application.Curr
ent.Dispatcher.InvokeAsync(() =>
WpfApp6/ViewModel/XrayImageVM.cs:73: Application.Current.Dispatcher.InvokeAsync(() =>
WpfApp6/ViewModel/XrayImageVM.cs:118: Application.Current.Dispatcher.InvokeAsync(() => ClearWindow());
WpfApp6/ViewModel/AxisEcatVM.cs:57: WeakReferenceMessenger.Default.Register<StartScanMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/AxisEcatVM.cs:63: WeakReferenceMessenger.Default.Register<StopScanMessage>(this, (recipient, msg) => Stop());
WpfApp6/ViewModel/AxisEcatVM.cs:64: WeakReferenceMessenger.De
fault.Register<UpdateTargetMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:101: WeakReferenceMessenger.Default.Register<LogMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:105: WeakReferenceMessenger.
Default.Register<BusStatusMessage>(this, (recipient, msg) =>
WpfApp6/ViewModel/MainShellVM.cs:109: WeakReferenceMessenger.Default.Register<ScanProgressMessage>(this, (recipient, msg) => {
WpfApp6/ViewModel/XrayImageVM.cs:25: WeakReferenceMessenger.D
efault.Register<AxisPositionReadyMessage>(this, (r, m) =>
WpfApp6/ViewModel/XrayImageVM.cs:29: WeakReferenceMessenger.D
efault.Register<StopScanMessage>(this, (r, m) =>
WpfApp6/ViewModel/AxisEcatVM.cs:303: try { WeakReferenceMesse
nger.Default.UnregisterAll(this); }catch { }
WpfApp6/ViewModel/MainShellVM.cs:148: try { WeakReferenceMess
enger.Default.UnregisterAll(this); }catch { }
WpfApp6/ViewModel/XrayImageVM.cs:127: try { WeakReferenceMess
enger.Default.UnregisterAll(this); }catch { }
WpfApp6/ViewModel/XrayImageVM.cs:129: //WeakReferenceMessenger.Default.UnregisterAll(this);
WpfApp6/ViewModel/AxisEcatVM.cs:97: _runningScanTask = Task.Run(() => RunScanTask(tx, ty, exposureMs, _cts.Token));
WpfApp6/ViewModel/AxisEcatVM.cs:214: Task.Run(async () =>
Archive/Legacy/AxisControlViewModel.cs:57: await Task.Delay(100);
WpfApp6/EtherCATMaster.cs:41: //Task.Delay(1000).Wait();
WpfApp6/EtherCATMaster.cs:56: await Task.Delay(500);
WpfApp6/ViewModel/AxisEcatVM.cs:87: // var wait = Task.WhenAny(_runningScanTask, Task.Delay(500));
WpfApp6/ViewModel/AxisEcatVM.cs:207: // Task.Delay(100).Wait(); // 等待任务响应取消
WpfApp6/ViewModel/AxisEcatVM.cs:218: var comp
leted = await Task.WhenAny(runningTask, Task.Delay(500)).ConfigureAwait(
false);
WpfApp6/ViewModel/AxisEcatVM.cs:229: //var wait = Task.WhenAny(runningTask, Task.Delay(500));
WpfApp6/ViewModel/AxisEcatVM.cs:291: var wait = Task.WhenAny(runningTask, Task.Delay(1000));
WpfApp6/XYScanAxis.cs:48: await Task.Delay(1500, token);
WpfApp6/XYScanAxis.cs:66: await Task.Delay(1000,token);
WpfApp6/XYScanAxis.cs:89: // await Task.Delay(200,token);
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$ • git grep -n -e ".Wait(" -- "*.cs" || true
• git grep -n -e ".Result" -- "*.cs" || true
bash: •: command not found
bash: •: command not found
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git grep -n -e ".Wait(" -- "*.cs" || true git grep -n -e ".Result" --
"*.cs" || true
WpfApp6/EtherCATMaster.cs:41: //Task.Delay(1000).Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:88: // wait.Wait()
;
WpfApp6/ViewModel/AxisEcatVM.cs:207: // Task.Delay(10
0).Wait(); // 等待任务响应取消
WpfApp6/ViewModel/AxisEcatVM.cs:230: //wait.Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:292: wait.Wait();
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git grep -n -e "\.Wait(" -- "*.cs" || true
git grep -n -e "\.Result" -- "*.cs" || true
git grep -n "new CancellationTokenSource" -- "*.cs" || true
git grep -n "Dispatcher.Invoke(" -- "*.cs" || true
git grep -n "async void" -- "*.cs" || true
WpfApp6/EtherCATMaster.cs:41: //Task.Delay(1000).Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:88: // wait.Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:207: // Task.Delay(100).Wait(); // 等待任务响应取消
WpfApp6/ViewModel/AxisEcatVM.cs:230: //wait.Wait();
WpfApp6/ViewModel/AxisEcatVM.cs:291: Task.Run(async (
)=> await Task.WhenAny(runningTask, Task.Delay(1000))).Wait();
WpfApp6/ViewModel/XrayImageVM.cs:120: try { op.Task.Wait(300); } catch { }
Archive/Legacy/AxisControlViewModel.cs:64: _cts = new CancellationTokenSource();
WpfApp6/ViewModel/AxisEcatVM.cs:93: _cts = new CancellationTokenSource();
WpfApp6/ViewModel/AxisEcatVM.cs:136: //_cts = new CancellationTokenSource();
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git checkout feature/bug
M WpfApp6/ViewModel/AxisEcatVM.cs
M WpfApp6/ViewModel/MainShellVM.cs
M WpfApp6/ViewModel/XrayImageVM.cs
Switched to branch 'feature/bug'
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git add .
11400@ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git commit -m " Start 在 StartScanInternal 创建 CTS,保存 _runni
ngScanTask;RunScanTask 的 finally 负责 Dispose CTS。
> "
[feature/bug d809288] Start 在 StartScanInternal 创建 CTS,保存 _runningScanTask;RunScanTask 的 finally 负责 Dispose CTS。
3 files changed, 32 insertions(+), 20 deletions(-)
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git checkout master
Switched to branch 'master'
11400@ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git merge feature/bug
Updating 9ac80b7..d809288
Fast-forward
WpfApp6/ViewModel/AxisEcatVM.cs | 27 ++++++++++++---------------
WpfApp6/ViewModel/MainShellVM.cs | 21 +++++++++++++++++----
WpfApp6/ViewModel/XrayImageVM.cs | 4 +++-
3 files changed, 32 insertions(+), 20 deletions(-)
11400@ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git checkout feature/bug
M WpfApp6/ViewModel/XrayImageVM.cs
Switched to branch 'feature/bug'
11400@ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git add .
11400@ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git commit -m "Xray 图像与轴到位不同步 增加防抖"
[feature/bug b232996] Xray 图像与轴到位不同步 增加防抖
1 file changed, 127 insertions(+), 40 deletions(-)
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git add .
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git commit -m "Xray 图像与轴到位不同步 增加防抖"
On branch feature/bug
nothing to commit, working tree clean
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git checkout master
Switched to branch 'master'
11400@ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git merge feature/bug
Updating d809288..b232996
Fast-forward
WpfApp6/ViewModel/XrayImageVM.cs | 167 +++++++++++++++++++++++++++++----------
1 file changed, 127 insertions(+), 40 deletions(-)
11400@ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git checkout feature/bug
M WpfApp6/Messages/AppMessages.cs
M WpfApp6/ViewModel/AxisEcatVM.cs
M WpfApp6/ViewModel/MainShellVM.cs
M WpfApp6/ViewModel/XrayImageVM.cs
M WpfApp6/XYScanAxis.cs
Switched to branch 'feature/bug'
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git add .
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git commit -m "还是不同步问题,现在好了 加了锁 在xysModel中"
[feature/bug b6fa6ec] 还是不同步问题,现在好了 加了锁 在xysModel中
5 files changed, 184 insertions(+), 37 deletions(-)
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git add .
11400@ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git commit -m "还是不同步问题,现在好了 加了锁 在xysModel中"
On branch feature/bug
nothing to commit, working tree clean
11400@▒▒ MINGW64 /d/vsprogram/WpfApp6 (feature/bug)
$ git checkout master
Switched to branch 'master'
11400@ MINGW64 /d/vsprogram/WpfApp6 (master)
$ git merge feature/bug
Updating b232996..b6fa6ec
Fast-forward
WpfApp6/Messages/AppMessages.cs | 29 +++++++++--
WpfApp6/ViewModel/AxisEcatVM.cs | 101 +++++++++++++++++++++++++++++++++------
WpfApp6/ViewModel/MainShellVM.cs | 13 +++--
WpfApp6/ViewModel/XrayImageVM.cs | 20 ++++++++
WpfApp6/XYScanAxis.cs | 58 ++++++++++++++++------
5 files changed, 184 insertions(+), 37 deletions(-)
11400@ MINGW64 /d/vsprogram/WpfApp6 (master)
$