0

I need a functionality that will allow to execute method in a background and leave window responsive, but I need to have a possibility to stop or suspend it anytime. I know that threads are partly answer to my question, but unfortunately there is no way to stop thread from executing a time-absorbing block of code just like that. I had thoughts about process communication, but is it a good idea? Or maybe there is a way to terminate a thread unconditionally?

user1013607
  • 115
  • 2
  • 13
  • how about threat.Abort()? – Dilshod Aug 20 '13 at 20:44
  • thread.Abort() doesn't guarantee that thread will be terminated instantly, even when it's currently doing something and I need exactly that. thread.suspend() works exactly the way I want, but abort() doesn't do the trick. – user1013607 Aug 20 '13 at 20:47
  • What is it doing? If it's messing with an external resource such as a file, you could end up corrupting it. – Tony Hopkinson Aug 20 '13 at 20:53

3 Answers3

0

The only option that you have, if it's important that you can always stop the code at any point in it's execution, and when you can't have cooperative cancellation on the part of the worker, then you need to have a separate process. It is the most reliable way of stopping the execution of code in the manor you've described. It cannot be reliably done using Threads.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • And is there any way to create process from window application that executes only particular method and get results of it? – user1013607 Aug 20 '13 at 20:52
  • @user1013607 No. You'd need to compile that method into an executable, and then use some form of inter process communication to handle the input/output of that function. – Servy Aug 20 '13 at 20:55
0

It seems that you're looking for BackgroundWorker .

You'll have to check in the second thread if the main thread is asking it to stop, and do so if needed.

Simple (tested) example:

public partial class Form1 : Form
{
    BackgroundWorker w = new BackgroundWorker();

    public Form1()
    {
        InitializeComponent();
        w.WorkerSupportsCancellation = true;
        w.DoWork += new DoWorkEventHandler(w_DoWork);
        w.RunWorkerAsync();
    }

    void w_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 0; i < 10000000000; i++)
        {
            if (w.CancellationPending)
            {
                MessageBox.Show("Cancelled");
                break;
            }
            //Do things...
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        w.CancelAsync();
    }
}

EDIT

If you're speaking of an HTTP request, perhaps: HttpWebRequest.Abort? (Though see this answer.)

Community
  • 1
  • 1
ispiro
  • 26,556
  • 38
  • 136
  • 291
  • This will not stop blocking code. Rather it's just a flag that get's raised that you manually have to process. Checking if worker.CancellationPending has been set to true through the method and stopping it wherever you are. So if you are doing something like an HTTP request and you don't want to wait for the request to timeout, you're still SOL if it already started. – corylulu Aug 20 '13 at 21:07
  • @Corylulu I assume the OP is not talking about such a case since he wrote: `I had thoughts about process communication`. – ispiro Aug 20 '13 at 21:09
  • Actually method in background does use the timeouts and this is the reason I need to have an ability to terminate it any time. – user1013607 Aug 20 '13 at 21:13
  • How about TerminateThread function from winapi? or maybe any other ways to make workaround? – user1013607 Aug 20 '13 at 21:17
  • As I described in my post: TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems: If the target thread owns a critical section, the critical section will not be released. If the target thread is allocating memory from the heap, the heap lock will not be released.... – corylulu Aug 20 '13 at 21:22
  • .... If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent. If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL. – corylulu Aug 20 '13 at 21:22
  • @user1013607 If you're speaking of an HTTP request, perhaps: [HttpWebRequest.Abort](http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.abort.aspx)? (Though see http://stackoverflow.com/a/9150785/939213.) – ispiro Aug 20 '13 at 21:23
  • @ispiro, no that was just my example. – corylulu Aug 20 '13 at 21:26
0

As stated in comments thread.Abort() is an option, but not advised. Rather you should be using thread.Interrupt() and the reasons for that are well detailed here.

The reason you should not kill a thread instantly is because it could cause a lock being set, but never unset due to the fact that the code was suddenly stopped with no path out. So if it locks code that you will need to reuse, there would be no way to unlock it from the previous call. Obviously, you could build around this, but I'm assuming you are using blocking code that isn't built from the ground up by you.

You could do it in a separate process and kill the process with much less risk, but then passing the data back and forth and the added mess becomes complicated.

Here is an example of using an external process to do this chuck and being able to kill the process will less risk.

public class Main
{
    public Main()
    {
        //In use
        DoCalculation calc = new DoCalculation();
        calc.StartCalculation(123, 188, ReceivedResults);

        //Cancel after 1sec
        Thread.Sleep(1000);
        calc.CancelProcess();
    }

    public void ReceivedResults(string result)
    {
        Console.WriteLine(result);
    }
}
public class DoCalculation
{
    private System.Diagnostics.Process process = new System.Diagnostics.Process();
    private Action<string> callbackEvent;
    public void StartCalculation(int var1, int var2, Action<string> CallbackMethod)
    {
        callbackEvent = CallbackMethod;
        string argument = "-v1 " + var1 + " -v2 " + var2;
        //this is going to run a separate process that you'll have to make and
        //you'll need to pass in the argument via command line args. 
        RunProcess("calcProc.exe", argument);
    }
    public void RunProcess(string FileName, string Arguments)
    {
        SecurityPermission SP = new SecurityPermission(SecurityPermissionFlag.Execution);
        SP.Assert();
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.FileName = FileName;
        process.StartInfo.Arguments = Arguments;
        process.StartInfo.WorkingDirectory = "";
        process.OutputDataReceived += ProcessCompleted;
        process.Start();
    }

    public void CancelProcess()
    {
        if (process != null)
            process.Kill();
    }

    private void ProcessCompleted(object sender, DataReceivedEventArgs e)
    {
        string result = e.Data;
        if (callbackEvent != null)
        {
            callbackEvent.Invoke(result);
        }
    }
}

Can you give us any more details on exactly what you are doing? Perhaps there are better alternatives to this problem.

Community
  • 1
  • 1
corylulu
  • 3,449
  • 1
  • 19
  • 35
  • The problem concerns a simple game. There is a form which uses results from method which is currently executed in different thread. But the method has timeouts and stopwatches and I would like to have an ability to stop it at any time, which is impossible when it waits stuck in while for some time. And I just need information about aborting the execution and let the player redo the calculations. – user1013607 Aug 20 '13 at 21:10
  • when you say it "waits stuck in while for some time", do you mean it's stuck in a while-loop? because if it's looping and the code isn't blocked waiting for a timeout, then you're fine using the BackgroundWorker method. – corylulu Aug 20 '13 at 21:19
  • well, it's while(stopatch.elapsed < x seconds) timeout, but i cannot put checking the thread state there, this function with loop cannot be changed. But it's being called from method executed in a different thread. – user1013607 Aug 20 '13 at 21:26
  • @user1013607 Okay, well although it's probably the case that restructuring the code could allow for this to work without separating the process, but I've updated code to show how to instantly kill the process with little risk. But it does involve separating the calculation part of the code to be used from a command line argument on a separate process altogether. – corylulu Aug 20 '13 at 21:41
  • @user1013607 However, if it's only YOUR code that is creating the timeout and you know that it shouldn't case issues. It might be possible to just use `Thread.Interrupt` safely. It's only when your waiting for external code that it's not safe to do this with, because you don't know if it will cause an issue. – corylulu Aug 20 '13 at 21:43
  • well it just does some simple maths and button invokes, sometimes I call a winapi method such as SetForegroundWindow, so do you think TerminateThread would be good idea? – user1013607 Aug 20 '13 at 21:57
  • 1
    Thread.Interrupt should work fine for that use. No reason to use Winapi for that. But you should post the full code because it sounds to me that you don't even need it to be interrupted like this. – corylulu Aug 20 '13 at 22:08