5

I've got a thread that goes out and looks up data on our (old) SQL server.

As data comes in, I post information to a modal dialog box - the user can't & shouldn't do anything else while all this processing is going on. The modal dialog box is just to let them see that I'm doing something and to prevent them from running another query at the same time.

Sometimes (rarely) when the code makes a call to the SQL server, the server does not respond (IT has it down for maintenance, the LAN line got cut, or the PC isn't on the network) or the person doing the query runs out of time. So, the modal dialog box does have a cancel button.

The Thread object (System.Threading.Thread) has IsBackground=true.

When someone clicks Cancel, I call my KillThread method.

Note: I can NOT use the BackgroundWorker component in this class because it is shared with some Windows Mobile 5 code & WM5 does not have the BackgroundWorker.

void KillThread(Thread th) {
  if (th != null) {
    ManualResetEvent mre = new ManualResetEvent(false);
    Thread thread1 = new Thread(
      () =>
      {
        try {
          if (th.IsAlive) {
            //th.Stop();
            // 'System.Threading.Thread' does not contain a definition for 'Stop'
            // and no extension method 'Stop' accepting a first argument of type
            // 'System.Threading.Thread' could be found (are you missing a using
            // directive or an assembly reference?)
            th.Abort();
          }
        } catch (Exception err) {
          Console.WriteLine(err);
        } finally {
          mre.Set();
        }
      }
    );
    string text = "Thread Killer";
    thread1.IsBackground = true;
    thread1.Name = text;
    thread1.Start();
    bool worked = mre.WaitOne(1000);
    if (!worked) {
      Console.WriteLine(text + " Failed");
    }
    th = null;
  }
}

In my Output Window, I always see "Thread Killer Failed" but no exception is ever thrown.

How should I stop a thread?

The best related posts I found where the two below:

How to Kill Thread in C#?
How to kill a thread instantly in C#?

EDIT:

There seems to be some confusion with the method I listed above.

First, when someone clicks the cancel button, this routine is called:

void Cancel_Click(object sender, EventArgs e) {
  KillThread(myThread);
}

Next, when I go in to kill a thread, I'd rather not have to wait forever for the thread to stop. At the same time, I don't want to let my code proceed if the thread is still active. So, I use a ManualResetEvent object. It should not take a full second (1000ms) just to stop a thread, but every time the WaitOne method times out.

Still listening for ideas.

Community
  • 1
  • 1

5 Answers5

7

Short Answer: You don't. Normally you do it by signaling you want to quit. If you're firing an SQL query, do it asynchronously (pardon my spelling), and cancel it if necessary. That really goes for any lengthy task in a separate thread.

For further reading see Eric Lippert's articles: Careful with that axe, part one: Should I specify a timeout? and Careful with that axe, part two: What about exceptions?

Edit: How do you call SQL Server? ADO, TDS, Standard/Custom Library, etc... ? THAT call should be made asynchrone. Thus: StartOpeningConnection, WaitFor OpeningComplete, StartQuery, WaitFor QueryComplete, Start CloseConnection, WaitFor CloseConnectionComplete etc. During any of the waits your thread should sleep. After waking up, Check if your parent thread (the UI thread) has cancelled, or a timeout has occurred and exit the thread and possibly inform sqlserver that you're done (closing connection).

It's not easy, but it rarely is...

Edit 2:In your case, if you are unable to change the database code to asynchrone, make it a seperate process and kill that if neccesary. That way the resources (connection etc.) will be released. With threads, this won't be the case. But it's an ugly hack.

Edit 3: You should use the BeginExecuteReader/EndExecuteReader Pattern. this article is a good reference: It will require rewriting your data access code, but it's the way to do it properly.

MarcelDevG
  • 1,377
  • 10
  • 10
  • I don't understand how I'd signal an sql query with an asynchronous quit request OR cancel that request... but I'm reading those 2 links of yours now. They are long, so if the answer is in there I'll have it one day. –  Feb 17 '11 at 21:36
  • OK, I read the article. My problem is that SQL Server 2000 does not have a way to abort a call, so I suppose the old server is buggy (by Eric Lippert's article). –  Feb 17 '11 at 22:10
  • So, what should I do? Call `Abort()` and use the `ManualResetEvent` to make my GUI return, or just set the thread to `null`? I'm not going to make everyone in the plant wait forever for a joined thread to terminate. –  Feb 17 '11 at 22:54
  • Hey Mark. I use standard sql calls. I can't find any information on these methods you've defined ("*StartOpeningConnection, WaitFor OpeningComplete, StartQuery, WaitFor QueryComplete, Start CloseConnection, WaitFor CloseConnectionComplete etc*"). Where are you getting those? –  Feb 21 '11 at 20:54
  • jp2code:"I use standard sql calls" What calls are you doing?? Are you using ado.net, thus sqlconnection, sqlcommand etc.? – MarcelDevG Feb 21 '11 at 23:29
  • Mark, I use something like (I hope this works...) DataTable table = new DataTable(); using (SqlConnection con = new SqlConnection(strConn)) { using (SqlCommand cmd = new SqlCommand(strSql, con)) { cmd.Connection.Open(); using (SqlDataReader r = cmd.ExecuteReader()) { table.Load(r); } cmd.Connection.Close(); /* redundant, maybe */ } } return table; –  Feb 25 '11 at 20:13
  • What you need is TCP Keepalive. – Joshua Nov 07 '16 at 17:00
2

I get the feeling that giving the Thread 1000ms to abort is simply not enough. MSDN recommends that you call Thread.Join. It would definitely be helpful to see the code that is being aborted.

Thread.Abort

The thread is not guaranteed to abort immediately, or at all. This situation can occur if a thread does an unbounded amount of computation in the finally blocks that are called as part of the abort procedure, thereby indefinitely delaying the abort. To wait until a thread has aborted, you can call the Join method on the thread after calling the Abort method, but there is no guarantee the wait will end.

ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
  • Well, it's got lots of custom designed objects that are being filled in a query. The query polls 6 different data tables (I did not design the system, I just write software for it). Before querying each table, I check to see if the Thread has bee aborted. Race condition, maybe, but that's all I've got. –  Feb 17 '11 at 21:19
  • Thread Join is a bad idea. If someone clicks cancel and I tell it to join the thread, the thread may never end and the GUI often gets locked on that step. –  Feb 17 '11 at 21:20
  • @jp2code - Thread.Join is overloaded to include a timeout parameter which will avoid that issue. – ChaosPandion Feb 17 '11 at 21:28
  • So, by specifying a timeout in Thread Join, I can get rid of the `ManualResetEvent`. That's good! I've coded it differently, but it still sits there doing NOTHING for the full wait time. –  Feb 17 '11 at 21:55
1

What are you passing into your KillThread method? The cancel button will be being clicked on the UI thread, not the one that you want to kill.

Mark Avenius
  • 13,679
  • 6
  • 42
  • 50
  • The button click method just calls `KillThread(Thread th)`. I didn't see a need in posting that. –  Feb 17 '11 at 21:16
  • @jp2code: you are holding a reference to the worker thread, right? That is what I was getting at :-) – Mark Avenius Feb 17 '11 at 21:18
0

You should signal your event when the user clicks Cancel (not kill the thread). In your example, the ManualResetEvent "mre"'s scope should be outside the thread function.

Erwin Alva
  • 403
  • 4
  • 17
  • I'm going to edit my post. I think I've worded this incorrectly. –  Feb 17 '11 at 21:21
  • The point here (as also MarcelDevG pointed out) is you don't just kill a thread. The thread should exit gracefully and clean-up after itself. – Erwin Alva Feb 17 '11 at 21:45
  • That would be idea. However, I have no way to tell our SQL Server 2000 to stop working on a query. I suppose I could simply abandon the thread (set it equal to NULL then create a new instance when the next query is performed), but I'd like to know how to stop a thread. –  Feb 17 '11 at 22:06
0

To answer the more general question of how to force kill any kind of Thread in C#:

If any unhandled Exception is thrown inside a thread (including those used by Task and other ways of running asynchronously), this thread will be terminated. However note that this comes with many problems, like resources not being freed, improper memory management, general undefined behavior etc, and the unhandled Exception may still have to be handled by its parent thread (wherever it was started from) OR by registering for the following Event beforehand, depending on how the thread was started:

AppDomain.CurrentDomain.UnhandledException += YourEventHandler;

I should emphasize again that this should be an absolute last resort. If you need this, your applications is almost certainly designed poorly and there are probably different solutions you should use. There are good reasons why Thread.Abort is now deprecated and no longer functional.

BasementScience
  • 358
  • 3
  • 7