我在C#中设计了MMVM模式。我的GUI有不同的按钮。每个按钮用于特定命令。这些命令派生自CommandsBase Class。每个命令都通过调用CommandExecute在单独的线程上运行。有几个命令,如CommandRunMode1,CommandRunMode2,commandDiagnosys等。现在已经出现了中止命令的新要求。我正在尝试编写CommandAbort类。问题是如何在GUI上按下ABORT按钮时中止已经执行的命令(即在CommandAbort类线程的中途停止其他线程)。
enter code here
#region COMMAND_Base
public abstract class CommandsBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected delegate void NoArgsDelegate();
protected delegate void OneArgDelegate(string arg1);
protected delegate void TwoArgDelegate(string arg1, bool arg2);
protected ViewModelBase ParentViewModel;
private StatusIndicator _isEnableState;
string _uiText;
bool _uiEnable;
RelayCommand _command;
protected Dispatcher _dispatcher;
private readonly int _responseDelay = 2000; // milliseconds
#region PROPERTIES
public CommandText CommandText { get; set; } // Ui Text
public CommandStatusIndicator CommandStatus { get; set; } // background color
public StatusIndicator IsEnableState
{
get { return _isEnableState; }
set
{
_isEnableState = value;
OnChanged("Status");
}
}
public string UiText
{
get { return _uiText; }
set
{
_uiText = value;
OnChanged("UiText");
}
}
public bool UiEnabled
{
get
{
return _uiEnable;
}
set
{
_uiEnable = value;
OnChanged("UiEnabled");
}
}
public ICommand Command
{
get
{
if (_command == null)
{
_command = new RelayCommand(param => this.CommandExecute(), param => this.CommandCanExecute);
}
return _command;
}
}
public int NumberOfAttempts;
public int ResponseDelay
{
get
{
return _responseDelay;
}
}
#endregion
protected CommandsBase()
{
}
protected void UpdateUi(string text)
{
UiText = text;
}
protected void UpdateUi(bool enabled)
{
UiEnabled = enabled;
}
protected void UpdateUi(string text, bool enabled)
{
UiText = text;
UiEnabled = enabled;
}
#region COMMAND_EXECUTION
public virtual void CommandExecute()
{
NoArgsDelegate commandExecution = new NoArgsDelegate(CommandExecuteInAThread);
commandExecution.BeginInvoke(null, null);
}
protected abstract void CommandExecuteInAThread();
public abstract bool CommandCanExecute { get; }
#endregion
public virtual void OnChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion
public class CommandRunMode1 : CommandsBase
{
public CommandRunMode1(string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Mode1
ApplicationLayer.TransmitString("START1");
while (Display.CurrentScreent != DisplayController.CurrentScreen.ST1SCREEN);
ApplicationLayer.TransmitString("MODE1ENTER");
while (Display.CurrentScreent != DisplayController.CurrentScreen.MD1SCREEN);
ApplicationLayer.TransmitString("PROCESSCREEN");
while (Display.CurrentScreent != DisplayController.CurrentScreen.PROCESSCREEN);
}
#endregion
}
public class CommandRunMode2 : CommandsBase
{
public CommandRunMode2(string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Mode2
ApplicationLayer.TransmitString("START2");
while (Display.CurrentScreent != DisplayController.CurrentScreen.ST2SCREEN);
ApplicationLayer.TransmitString("MODE2ENTER");
while (Display.CurrentScreent != DisplayController.CurrentScreen.MD2SCREEN);
ApplicationLayer.TransmitString("PROCESSCREEN");
while (Display.CurrentScreent != DisplayController.CurrentScreen.PROCESSCREEN);
}
#endregion
}
public class CommandAbort : CommandsBase
{
public CommandAbort (string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Abort currently running command
ApplicationLayer.TransmitString("ABRT");
}
#endregion
}
答案 0 :(得分:4)
取消线程应该始终合作。
我的意思是什么?
线程中运行的代码应定期检查是否应该继续。这可能是某处的布尔值。如果需要取消,只需清理您正在使用的资源并return
。
volatile bool IsCancelled = false;
void DoWork()
{
while(!IsCancelled)
{
//do work
}
}
此标志何时设置?
也许你有一个取消按钮。推送它会触发事件处理程序。此事件处理程序设置标志。下一次线程代码检查标志时,它将取消操作。这就是它被称为合作取消的原因。 2个线程协同工作以实现它。
我担心您需要克服架构挑战
在MVVM中,命令是将用户交互传递给视图模型的管道。 View模型应该通过调用其他类上的相应方法(您的模型/基础层/业务对象...使用您喜欢的任何单词)来对此命令做出反应。您需要实现DelegateCommand或RelayCommand。
为什么要用这种方式构建它?现在,您可以在视图模型中使用取消标记。这是视图模型的用途之一 - 存储状态!
如果你能够,我建议使用.Net的任务并行库。它支持cooperative cancellation for free!
总结一下。我强烈建议您将串行代码移出命令类。它可以使它适用于您的设计,但这不是一个好的做法:(