user1598715
user1598715

Reputation: 13

How to stop Execution of Thread from other thread?

I have designed MMVM pattern in C#. My GUI has different button. Each button is for particular command. These commands are derived from CommandsBase Class. Each command runs on seperate thread by calling CommandExecute. There are several commands like CommandRunMode1, CommandRunMode2, commandDiagnosys etc. Now new requirement has been arised to abort command. I am trying to write CommandAbort Class. And the problem is how to abort already executing command when ABORT button is pressed on GUI (i.e. stop other thread in the halfway from CommandAbort class thread).

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

region 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

region 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

region 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

Upvotes: 0

Views: 391

Answers (1)

Gusdor
Gusdor

Reputation: 14332

Cancellation of a thread should always be cooperative.

What do i mean by that?

The code running in your thread should periodically check to see if it should continue. This may be a boolean somewhere. If cancellation is required, simply cleanup the resources you are using and return.

volatile bool IsCancelled = false;

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

When is this flag set?

Perhaps you have a Cancel button. Pushing this would trigger an event handler. This event handler sets the flag. The next time your threaded code checks the flag, it will cancel the operation. This is why it is called cooperative cancellation. 2 threads work together to make it happen.


Im afraid you have an architectural challenge to overcome

In MVVM, commands are the pipeline in which user interaction is communicated to the view model. The View model should react to this command by 'doing to the work' by calling the appropriate methods on other classes ( your model/foundation layer/business objects...use whichever words you prefer). You will need an implementation of DelegateCommand or RelayCommand.

  1. User clicks Button
  2. Button executes command
  3. Command invokes method on view model
  4. View model calls your domain classes to do the work

Why structure it this way? Now you can have your cancellation flag in the view model. This is one of the things a view model is for - storing state!

If you are able to, I recommend using .Net's Task Parallel Library. It supports cooperative cancellation for free!

To sum up. I strongly recommend you move the serial code out of the command classes. It is possible to make it work with your design but this is not good practice :(

Upvotes: 4

Related Questions