WPF中异步函数调用失效问题求助
首先,你的核心问题出在ExecuteJob方法的循环逻辑、共享变量的线程安全问题上,再结合.NET Framework与.NET Core在内存模型、线程调度的细微差异,加上NuGet打包的潜在兼容性问题,最终导致任务无法正常完成。下面分点拆解并给出解决办法:
1. 共享变量的可见性问题:线程无法感知变量变更
你的ExecuteJob里有两个关键循环:
Do While Me.JobStart = True 'Warten bis das Startbit von der SPS zurückgesetzt wurde Loop _JobIsRunning = True Do While _JobIsRunning = True Loop
这里的JobStart和_JobIsRunning都是普通字段,在多线程场景下(你用Task.Run把ExecuteJob放到线程池线程执行),.NET Framework的JIT编译器可能会对这些变量做缓存优化——线程会一直读取本地缓存的变量值,而非内存中的最新值。这就解释了为什么你能看到I/O变量变化,但任务永远卡在循环里无法完成。
解决方案:确保变量的线程可见性
把JobStart和_JobIsRunning声明为Volatile,强制线程每次读取都从内存获取最新值:
<Volatile()> Public Property JobStart As Boolean <Volatile()> Private _JobIsRunning As Boolean
或者在循环中使用Thread.VolatileRead来读取变量:
Do While Thread.VolatileRead(Of Boolean)(Me.JobStart) = True Loop
2. 空循环耗尽线程池资源(.NET Framework下更显著)
.NET Framework的线程池调度策略和.NET Core不同,空循环会持续占用线程池线程,既导致CPU空转,也可能让线程无法及时响应变量变更。建议在循环中加入短暂延迟,让出线程资源:
Do While Thread.VolatileRead(Of Boolean)(Me.JobStart) = True ' 让出线程,避免CPU占用过高 Thread.Sleep(10) ' 同步方法中用Thread.Sleep,异步场景可改用Task.Delay Loop
第二个_JobIsRunning的循环也需要添加同样的延迟逻辑。
3. NuGet打包的兼容性问题
你提到直接引用类库正常,但NuGet安装失效,这大概率是NuGet包没有正确包含.NET Framework 4.7.2的编译输出,或者打包配置有问题。
解决方案:检查并修复NuGet打包配置
- 确保类库项目的
csproj正确声明多目标框架:
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
- 使用
dotnet pack命令重新打包,确保两个框架的编译输出都被包含进NuGet包。 - 清理本地NuGet缓存,避免旧缓存文件干扰:
dotnet nuget locals all --clear
4. 简化异常处理逻辑
在ExecuteJobAsync中,你手动检查任务故障并抛出异常的逻辑可以简化——Await会自动传播任务的异常,不需要手动判断:
Public Async Function ExecuteJobAsync() As Task Await Task.Run(AddressOf ExecuteJob) ' Await会自动抛出任务中的异常,无需手动处理 End Function
验证步骤
- 先修改共享变量的可见性并添加循环延迟,直接引用类库到.NET Framework 4.7.2项目测试,确认问题是否解决。
- 如果直接引用正常,再重新打包NuGet包、清理缓存后安装测试。
内容的提问来源于stack exchange,提问作者Stef

