-1

Today I got a Problem with a Deadlock in a App I developed. In particular I am stopping a Window which started a Thread in the Background.

Problem? On Closing of the Window the Window closed, but the Process was still alive in the Background. I thought about if the Thread itself blocks, but as a Background-declared Thread it should normaly shut down anyway. But then I noticed that "Logic.DoSomething()" started it's own endless-Threads without declaring it as Background-Threads.

So why is a (non-Background) Thread in a (Background) Thread blocking the Process? Shouldn't it shutdown because it's "Parent" runs as Background-Thread? And if not, why doesn't block the Parent-Thread itself?

public partial class MainWindow : Window
{
    private Thread TheThread { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        Loaded += MainWindow_Loaded;
        Closed += MainWindow_Closed;
    }

    private void MainWindow_Loaded(object sender, EventArgs e)
    {
        // Create the (background-)thread and start it
        TheThread = new Thread(() => Logic.DoSomething());
        TheThread.IsBackground = true;
        TheThread.Start();
    }

    private void MainWindow_Closed(object sender, EventArgs e)
    {
        // Close Thread if existent.
        TheThread.Abort("Window was closed");
        TheThread.Join(500);        
    }
}

PS: Please note, that I don't use a Worker-Object because I handle the Abort-Exception in the Thread normaly.

Edit: It is a theoretical Question for a Windows Presentation Foundation-Project (WPF) and not for an Windows Forms Project. Besides of that the Basic-Question was NOT how to Start and Stop a Thread using e.g. CancellationToken. It was a Question why a Thread made by a Process doesn't kill the Process it called or is gettin stuck in the Join. Hope this is clear enough.

TheC
  • 15
  • 4
  • 3
    You really shouldn't be aborting the thread in the first place. You should be using cooperative cancellation in order to safely shut the operation down in a reliable manner. – Servy Dec 06 '16 at 14:51
  • Why aren't you using `Task.Run`? And pass a CancellationToken that would allow you to signal that you want that other task to cancel? – Panagiotis Kanavos Dec 06 '16 at 15:04
  • BTW a CancellationToken is the proper way to signal any thread that you want to cancel. Much easier to use than eg a ManualResetEvent – Panagiotis Kanavos Dec 06 '16 at 15:05
  • @PanagiotisKanavos Really? Why? – Thorsten Dittmar Dec 06 '16 at 15:06
  • @ThorstenDittmar It semantically represents that exact operation. It's specifically designed for that exact purpose, so it's operations clearly represent exactly the relevant operations for that context, and it doesn't contain operations that wouldn't make sense for an item responsible for cancellation. It also allows you to separate the thing being cancelled from the thing doing the cancelling, making it clear to someone reading the code who is responsible for what. – Servy Dec 06 '16 at 15:08
  • @ThorstenDittmar the [doc](https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx) explain it better, but a huge benefit is that you decouple *requesting* a cancellation from a CancellationTokenSource and *listening/responding* to it by any number of threads. You can't do that easily with a ManualResetEvent. You can pass a token down any number of nested calls, other cooperating threads, other tasks etc. and all would know how to treat it – Panagiotis Kanavos Dec 06 '16 at 15:13
  • Possible duplicate of [How do I stop a thread when my winform application closes](http://stackoverflow.com/questions/3542061/how-do-i-stop-a-thread-when-my-winform-application-closes) – Victor Zakharov Dec 06 '16 at 16:13

2 Answers2

2

Instead of calling Abort on the thread, which is never a good idea, communicate with the thread and indicate it should stop. Then wait for the thread to stop. A good means of communicating things like this are wait handles. In the following example I'll use a ManualResetEvent:

public partial class MainWindow : Window
{
    private ManualResetEvent theThreadShouldStop;
    private Thread TheThread { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        // ??? This should be set through designer!!
        Loaded += MainWindow_Loaded;
        Closed += MainWindow_Closed;

        theThreadShouldStop = new ManualResetEvent(false);
    }

    private void MainWindow_Loaded(object sender, EventArgs e)
    {
        // Create the (background-)thread and start it
        TheThread = new Thread(() => Logic.DoSomething(theThreadShouldStop));
        TheThread.IsBackground = true;
        TheThread.Start();
    }

    private void MainWindow_Closed(object sender, EventArgs e)
    {
        // Close Thread if existent.
        theThreadShouldStop.Set();
        TheThread.Join();        
        theThreadShouldStop.Close();
    }
}

Now the thread method needs to be cooperative, but as I don't know what the thread method does, I'll give an example of a common scenario where the thread runs in a loop that should be cancellable:

public class Logic
{
    public void DoSomething(ManualResetEvent cancel)
    {
        while (!WantHandle.WaitOne(cancel, 1))
        {
           ... Do stuff
        }
    }
}       
Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • 1
    .NET has the CancellationToken and CancellationTokenSource for this. Many BCL methods already accept a CancellationToken. – Panagiotis Kanavos Dec 06 '16 at 15:06
  • So? What's the big difference? – Thorsten Dittmar Dec 06 '16 at 15:07
  • 1
    That it's well known, built for this purpose, supports querying *and/or* raising exceptions. All of the things you'd need for cooperative cancellation. The [docs](https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx) explain more on this, eg the decoupling achieved between requisting and listening to cancellation – Panagiotis Kanavos Dec 06 '16 at 15:07
  • Yes, you can use it as well, but you can also use my above approach. Both work. Neither is wrong. Feel free to write your own answer outlining the `CancellationToken` approach. – Thorsten Dittmar Dec 06 '16 at 15:10
  • I wouldn't use a raw thread at all. I'd use `Task.Run` or a more suitable construct. – Panagiotis Kanavos Dec 06 '16 at 15:14
  • That's another thing I tend to disagree about :-) To me, a `Task` is some action that should be performed in parallel. It does a couple of things and then ends. A `Thread`, however, to me is something that could run endlessly, for example polling some source, etc. I know that technically a `Task` can be used for this, too, but when you talk about semantics for `CancellationToken`, allow me to talk about semantics here ;-) – Thorsten Dittmar Dec 06 '16 at 15:19
  • As I said "another more suitable construct". Wasting a thread to poll is .. wasteful. That's what thread timers are for. – Panagiotis Kanavos Dec 06 '16 at 15:21
  • The discussion is futile as we don't know what his thread does. Polling was just an example. All I'm saying is that neither, `Task` nor `Thread` are the ultimate solution for everything. – Thorsten Dittmar Dec 06 '16 at 15:25
1

Threads don't have "parents". There is no hierarchy. They don't know or care what thread started them. They're just threads, and the application ends when no non-background threads are running; that's all there is to it. Threads block until another thread ends when you ask them to by calling Join. They don't just do things on their own without you telling them to. They do exactly what you tell them to.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • join doesn't affect when a thread terminates, your answer is puzzling – David Haim Dec 06 '16 at 14:56
  • @DavidHaim If you `Join` on another thread, then that thread won't terminate (or even continue executing other operations) until after that thread has terminated (unless you use a timeout). If they want a given thread to not end until another thread has finished, `Join` is exactly how they can accomplish that. – Servy Dec 06 '16 at 14:57
  • @DavidHaim The sentence is a bit confusing. What he's trying to say is: Use `Join` to block a thread until the other thread ends after you asked it to end. – Thorsten Dittmar Dec 06 '16 at 14:58
  • @Servy, I'm aware of that, believe me , but your answer make it sounds like join somehow affects the one you want to be join on – David Haim Dec 06 '16 at 14:59
  • @DavidHaim How so? If you want a thread to block until another thread ends, you call `Join` from the thread, and it will block until that thread has finished. – Servy Dec 06 '16 at 15:01
  • @Servy: " Threads block until another thread ends when you ask them to by calling Join" it sounds like a thread dies, then blocks until you join it. re-phrase it – David Haim Dec 06 '16 at 15:01
  • @DavidHaim But that's not what that says at all. That's not in any way a reasonable interpretation of that statement. – Servy Dec 06 '16 at 15:06