调用异步方法后,进程似乎没有回到 WPF 应用程序中的主线程
Posted
技术标签:
【中文标题】调用异步方法后,进程似乎没有回到 WPF 应用程序中的主线程【英文标题】:After calling an async method the process don't seem to go back to main thread in WPF app 【发布时间】:2021-04-23 22:56:52 【问题描述】:我对 WPF 和线程都是新手,这部分是基于我没有编写的代码,所以我什至无法提出正确的问题或不知道去哪里寻找。
我有一个带有切换按钮的应用程序,可以停止和启动一些服务。
当您启动应用程序时,服务正在运行。然后,您可以单击按钮停止它们。 在等待服务停止(或启动)时,该按钮被禁用。 当所有服务都从一种状态转换到另一种状态时,按钮上的文本会发生变化,并且应该启用按钮,除非它没有,文本正在更改,但按钮不会启用。但是,如果我单击 GUI 上的任意位置,按钮就会启用。 这让我认为点击以某种方式(重新)激活了一个线程。
代码注释
在构造函数中,我参考CanServicesExecute()方法来判断按钮是否启用。 所以当程序运行时,这个方法一直被系统击中。
检查服务是否正在运行的部分由单独的线程完成,以便不阻止 GUI 的其余部分,而仅禁用一个按钮。它每 5 秒检查一次。
在这段代码中,我用简单的等待代替了实际检查服务是否已终止或停止,但我的问题仍然存在:在 Thread.Sleep 之后,正在设置 Servicesstate,并且按钮正在获取另一个文本,但是CanServicesExecute() 不再被击中。直到我单击 GUI 中的某个位置,然后它又被不断地击中。
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Contracts.Annotations;
using Contracts.BusinessObjects;
using eDeployment.Properties;
using eTray.Common;
using eTray.Server;
using eTray.Types.Server;
using Infrastructure.Commands;
using Settings = eDeployment.Properties.Settings;
namespace eDeployment.ViewModel
public class DeploymentViewModel : INotifyPropertyChanged
enum ServicesState
Running = 0,
Terminated = 1,
Transitioning = 2,
Unfinished = 3
private ServicesState IsServicesRunning get; set;
public DeploymentViewModel()
StopStartServicesCommand = new RelayCommand<object>(_ => StopStartServices(), _ => CanServicesExecute());
private void StopStartServices()
if (IsServicesRunning == ServicesState.Running)
StopServicesCommand();
else
StartServicesCommand();
private async void StopServicesCommand()
SetServiceState(ServicesState.Transitioning, Resources.TryStopServices);
//CheckServicesX(false);
CheckServicesProcessX(true);
private async void StartServicesCommand()
SetServiceState(ServicesState.Transitioning, Resources.TryStartServices);
LogWrite(Resources.TryStartServices, false);
//CheckServicesX(true);
CheckServicesProcessX(true);
private async void CheckServicesProcessX(bool starting)
await Task.Run(() => CheckServicesX(starting))
.ContinueWith(t => , TaskScheduler.FromCurrentSynchronizationContext());
private void CheckServicesX(bool starting)
Thread.Sleep(3000);
if (!starting)
SetServiceState(ServicesState.Terminated, Resources.ServicesTerminated);
LogWrite(Resources.ServicesTerminated, false);
else
SetServiceState(ServicesState.Running, Resources.ServicesRunning);
LogWrite(Resources.ServicesRunning, false);
private async void CheckServicesY(bool starting)
await Task.Delay(3000);
if (!starting)
SetServiceState(ServicesState.Terminated, Resources.ServicesTerminated);
LogWrite(Resources.ServicesTerminated, false);
else
SetServiceState(ServicesState.Running, Resources.ServicesRunning);
LogWrite(Resources.ServicesRunning, false);
private void SetServiceState(ServicesState servicestate, string message)
switch (servicestate)
case ServicesState.Running:
IsServicesRunning = ServicesState.Running;
ColorIndicator = "Green";
ServicesLabel = Resources.LabeL_StopServices;
break;
case ServicesState.Transitioning:
IsServicesRunning = ServicesState.Transitioning;
ColorIndicator = "Orange";
break;
case ServicesState.Terminated:
IsServicesRunning = ServicesState.Terminated;
ServicesLabel = Resources.LabeL_StartServices;
ColorIndicator = "Red";
break;
case ServicesState.Unfinished:
IsServicesRunning = ServicesState.Terminated;
ColorIndicator = "Orange";
break;
ServiceStatusText = message;
private bool CanServicesExecute()
if (IsServicesRunning == ServicesState.Transitioning)
return false;
return true;
【问题讨论】:
如果 som UI 组件未在 WPF 中更新,最常见的原因是未引发 OnPropertyChanged 事件,或使用不正确的参数引发。 你能显示 xaml 吗?什么是 IsEnabled 绑定? 正在引发 OnPropertyChanged。如果我同步运行它,它应该可以正常工作,但是 GUI 在整个持续时间内都被锁定。看起来这不是返回ui线程,而是FromCurrentSynchronizationContext不是ui线程吗?等待 Task.Run(() => CheckServicesX(starting)) .ContinueWith(t => , TaskScheduler.FromCurrentSynchronizationContext()) 【参考方案1】:您需要确保在您想要刷新绑定到StopStartServicesCommand
的Button
的状态时调用CanServicesExecute
。您可以通过引发命令的 CanExecuteChanged
事件来做到这一点。
如何引发事件取决于您正在使用的RelayCommand<T>
类的实现。它应该有一个引发事件的公共方法:
StopStartServicesCommand.RaiseCanExecuteChanged();
在CheckServicesX
中调用此函数,或者在您想根据当前状态启用或禁用Button
时调用此函数。
【讨论】:
所以它没有引发事件的方法,但我用谷歌搜索该事件基本上只是调用 CommandManager.InvalidateRequerySuggested();所以我打电话给它,它奏效了。谢谢以上是关于调用异步方法后,进程似乎没有回到 WPF 应用程序中的主线程的主要内容,如果未能解决你的问题,请参考以下文章