如何从其他线程停止执行线程?

时间:2013-12-23 12:04:25

标签: c# wpf multithreading abort

我在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

区域COMMAND_RunMode1

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

}

endregion

区域COMMAND_RunMode2

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

}

endregion

区域COMMAND_Abort

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

}

endregion

1 个答案:

答案 0 :(得分:4)

取消线程应该始终合作

我的意思是什么?

线程中运行的代码应定期检查是否应该继续。这可能是某处的布尔值。如果需要取消,只需清理您正在使用的资源并return

volatile bool IsCancelled = false;

void DoWork()
{
    while(!IsCancelled)
    {
        //do work
    }
}

此标志何时设置?

也许你有一个取消按钮。推送它会触发事件处理程序。此事件处理程序设置标志。下一次线程代码检查标志时,它将取消操作。这就是它被称为合作取消的原因。 2个线程协同工作以实现它。


我担心您需要克服架构挑战

在MVVM中,命令是将用户交互传递给视图模型的管道。 View模型应该通过调用其他类上的相应方法(您的模型/基础层/业务对象...使用您喜欢的任何单词)来对此命令做出反应。您需要实现DelegateCommand或RelayCommand。

  1. 用户点击按钮
  2. 按钮执行命令
  3. Command调用视图模型上的方法
  4. 查看模型调用您的域类来完成工作
  5. 为什么要用这种方式构建它?现在,您可以在视图模型中使用取消标记。这是视图模型的用途之一 - 存储状态!

    如果你能够,我建议使用.Net的任务并行库。它支持cooperative cancellation for free

    总结一下。我强烈建议您将串行代码移出命令类。它可以使它适用于您的设计,但这不是一个好的做法:(