I'm having a problem with threads. My code:
Task.Factory.StartNew(() =>
{
cts = new CancellationTokenSource();
var lines = File.ReadLines(Path.Combine(Environment.CurrentDirectory, "urls.txt"));
try
{
var q = from line in lines.AsParallel().WithDegreeOfParallelism(30).WithCancellation(cts.Token)
let result = Parse(line, cts.Token)
select new
{
res = result
};
foreach (var x in q)
{
if (x != null)
{
Console.WriteLine("{0}", x.res);
}
}
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
});
Now in Parse I have:
public String Parse(String url,CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
/* many lines of code */
InputForm iForm = new InputForm();
iForm.setPageData(pageData);
if (iForm.ShowDialog() == DialogResult.OK)
{
string userInput = iForm.textBox.Text;
/* code block */
return result;
} else {
return Parse(newUrl,ct);
}
}
I'm using ShowDialog because I need to get user input from iForm (this form has a timer and is auto closed after 60 seconds). Now, when I opened about 30 forms and click Cancel (on main form) this dialog forms need to be closed manualy. Is it posible to close this form after clicking Cancel?
I do this a lot.
What you're going to need to do is
create a way to communicate with your Main Thread Of Execution (MTOE)
call your main thread and wait for the response
in your main thread, display your dialog box
set the return value for your thread
signal your thread that you are done
A custom event handler works great for getting a message from your thread back to the MTOE.
A ManualResetEvent is good for your thread to know when the MTOE is complete.
A class instance can be passed in an event handler that the MTOE uses to fill a few data items and pass back to the thread whenever it is done.
Typically, when I create my special class, it contains the event handler and the ManualResetEvent object.
From your MTOE, if you close your form, you can signal all of your waiting dialog boxes to Cancel.
This would require a little redesign, but I think it would give you what you are after.
You may want to look at http://msdn.microsoft.com/en-us/library/system.windows.forms.application.openforms.aspx
You could iterate over the open open forms and call close on those that are of type InputForm
EDIT:
The below comment is correct this would throw an exception. You would actually need something like FormToClose.BeginInvoke(delegate ()=> FormToClose.Close());
Related
I have a simple C# winform app where I spawn a new thread to show another winform. After a process is completed i want to close that form using the below code. The issue I have is that when I call busyForm.BeginInvoke it is bypassing the null check and throw and error. How to correctly close the winform in another thread?
static Indicator busyForm;
public static async Task Execute()
{
Thread busyIndicatorthread = new Thread(new ThreadStart(()=>FormThread()));
busyIndicatorthread.SetApartmentState(ApartmentState.STA);
busyIndicatorthread.Start();
}
private static void FormThread()
{
busyForm = new Indicator();
busyForm.Closed += (sender2, e2) => busyForm.Dispatcher.InvokeShutdown();
Dispatcher.Run();
}
public static Task Execute(){
Thread busyIndicatorthread = new Thread(new ThreadStart(()=>FormThread(hwind)));
busyIndicatorthread.SetApartmentState(ApartmentState.STA);
busyIndicatorthread.Start();
// dos some stuff
if (busyForm != null)
{
busyForm.BeginInvoke(new System.Action(() => busyForm.Close())); <--- throw null error
busyForm = null;
}
}
That is because before calling .Close() method, time has passed and it is not assured that busyForm exists anymore.
In fact, it is possible that, while the new System.Action(() => busyForm.Close() thread is starting, you main thread goes to busyForm = null;.
You can try moving the null to secondary thread.
if (busyForm != null)
{
busyForm.BeginInvoke(new System.Action(() =>
{
lock(busyForm){
busyForm.Close();
busyForm = null;
}
}));
}
Almost no application starts another message pump to display notifications. It's not needed. In all applications, the busy and progress dialog boxes are generated and displayed.by the UI thread. Operations that could block are performed in the background, eg in a background thread or far better, using async/await and Task.Run. The UI is updated using events or callbacks, eg using the Progress< T> class.
In this case though, it seems all that's needed is to display a form before a long-running task and hide it afterward:
public async void btnDoStuff_Async(object sender, EventArgs args)
{
//Disable controls, display indicator, etc
btnDoStuff.Enabled=false;
using var busyForm = new Indicator();
busyForm.Show();
try
{
var result=await Task.Run(()=> ActuallyDoStuffAndReturnResult());
//Back in the UI form
//Do something with the result
}
finally
{
//Close the busy indicator, re-enable buttons etc.
busyForm.Close();
btnDoStuff.Enabled=true;
}
}
The finally block ensures the UI is enabled and the busy form hidden even in case of error.
20+ years ago some Visual Basic 6 applications did start another Window message pump to act as a "server". Visual Basic 6 threading was very quirky, so people used various tricks to get around its limitations.
When you write this code:
busyForm.BeginInvoke(new System.Action(() => busyForm.Close())); <--- throw null error
busyForm = null;
The order in which it executes is almost certainly this:
busyForm = null;
busyForm.Close();
No wonder you're getting a null reference exception!
Simply set the form to null in your invoke. That'll fix it.
However, the correct way to do this is as Panagiotis Kanavos suggests.
Suppose I have a method that execute a web scraping for populate a combobox when the program start. The scenario is that the user start the software but there is no connection, so no data will be inserted in the combobox. I'm trying to execute that method in a while until the connectionStatus variable value change, a little example:
Thread connThread;
bool connectionStatus = false;
try
{
connThread = new Thread(x =>
{
while (!connectionStatus)
{
if(connectionStatus) break;
GetDataFromWeb();
}
});
connThread.Start();
}
catch (Exception e)
{
//There is no connection
connectionStatus = false;
connThread. //There is no SleepMethod ??
}
I need to display the application but in the mainwhile I need to suspend for a certain second the thread connThread, the problem's that I can't find any Sleep method, why?
Use Thread.Sleep(). It is a static method of the Thread class.
Thread.Sleep(1000); // sleeps for 1 second
Calling this suspends the calling thread, so if you want to sleep inside your thread, put the try/catch inside the thread and call Thread.Sleep there.
For example, how I would do it:
var connThread = new Thread(x =>
{
while(true) {
try {
GetDataFromWeb();
break;
} catch {
// sleep for 1 second
// and then try again
Thread.Sleep(1000);
}
}
});
connThread.Start();
Also, be careful when adding items to the ComboBox, because all UI stuff must be done on the UI thread. In WPF, you can call Application.Current.Dispatcher.Invoke() to invoke the UI thread.
My program works like this:
I press a radio button which opens the port.
Next i press a button "Read" which starts a thread that reads data continously from the Serial Port using port.ReadLine() and prints it in a textbox;
I have another radio which should first join the thread and after that close the port;the problem is the printing goes well until i close the port when the UI freezes.
public Form1()
{
mythread = new Thread(ReadFct);
myPort = new SerialPort("COM3", 9600);
myPort.ReadTimeout = 3500;
InitializeComponent();
foreach (var t in Constants.ComboParameters)
this.paramCombo.Items.Add(t);
radioClose.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
radioOpen.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
}
Below is the function attached to the thread
void ReadFct()
{
string aux = "";
while (readCondition)
{
if (myPort.IsOpen)
aux = myPort.ReadLine();
this.SetText(aux);
}
}
Below is the radio button event handler
public void radioButtonCheckedChanged(object sender,EventArgs e)
{
if (radioOpen.Checked && !myPort.IsOpen)
try
{
myPort.Open();
mythread.Start();
}
catch (Exception)
{
MessageBox.Show("Nu s-a putut deschide port-ul");
}
if (radioClose.Checked && myPort.IsOpen)
{
readCondition = false;
mythread.Join();
myPort.Close();
// myPort.DataReceived -= DataReceivedHandler;
}
}
The read button function:
private void readbtn_Click(object sender, EventArgs e)
{
if (!myPort.IsOpen)
MessageBox.Show("PORT NOT OPENED!");
else
{
// myPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
readCondition = true;
if (!mythread.IsAlive)
{
mythread = new Thread(ReadFct);
mythread.Start();
}
}
I have used what MSDN suggest when changing control from another thread:
private void SetText(string text)
{
if (this.textBox1.InvokeRequired)
{
StringTb del = new StringTb(SetText);
this.Invoke(del, new object[] { text });
}
else
SetData = text;
}
It's hard to know exactly what you need, lacking a good Minimal, Complete, and Verifiable code example to illustrate the question. That said, the issue here is that the Thread.Join() method causes that thread to stop doing any other work, and the thread you use to call that method is the thread that handles all of the user interface. Worse, if your port never receives another newline, the thread you're waiting on will never terminate, because you're stuck waiting on the ReadLine() method. Even worse, even if you do get a newline, if that happens while you're stuck waiting on the Thread.Join(), the call to Invoke() will deadlock, because it needs the UI thread to do its work, and the Thread.Join() call is preventing it from getting the UI thread.
In other words, your code has multiple problems, any one of which could cause problems, but all of which together mean it just can't possibly work.
There are a variety of strategies to fix this, but IMHO the best is to use await. The first step in doing that is to change your I/O handling so that it's done asynchronously instead of dedicating a thread to it:
// Ideally, you should rename this method to "ReadFctAsync". I am leaving
// all names intact for the same of the example though.
async Task ReadFct()
{
string aux = "";
using (StreamReader reader = new StreamReader(myPort.BaseStream))
{
while (true)
{
aux = await reader.ReadLineAsync();
// This will automatically work, because the "await" will automatically
// resume the method execution in the UI thread where you need it.
this.SetText(aux);
}
}
}
Then, instead of creating a thread explicitly, just create a Task object by calling the above:
public Form1()
{
// In this approach, you can get rid of the "mythread" field altogether
myPort = new SerialPort("COM3", 9600);
myPort.ReadTimeout = 3500;
InitializeComponent();
foreach (var t in Constants.ComboParameters)
this.paramCombo.Items.Add(t);
radioClose.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
radioOpen.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
}
public async void radioButtonCheckedChanged(object sender,EventArgs e)
{
if (radioOpen.Checked && !myPort.IsOpen)
{
try
{
myPort.Open();
await ReadFct();
// Execution of this method will resume after the ReadFct() task
// has completed. Which it will do only on throwing an exception.
// This code doesn't have any continuation after the "await", except
// to handle that exception.
}
catch (Exception)
{
// This block will catch the exception thrown when the port is
// closed. NOTE: you should not catch "Exception". Figure out what
// *specific* exceptions you expect to happen and which you can
// handle gracefully. Any other exception can mean big trouble,
// and doing anything other than logging and terminating the process
// can lead to data corruption or other undesirable behavior from
// the program.
MessageBox.Show("Nu s-a putut deschide port-ul");
}
// Return here. We don't want the rest of the code executing after the
// continuation, because the radio button state might have changed
// by then, and we really only want this call to do work for the button
// that was selected when the method was first called. Note that it
// is probably even better if you just break this into two different
// event handlers, one for each button that might be checked.
return;
}
if (radioClose.Checked && myPort.IsOpen)
{
// Closing the port should cause `ReadLineAsync()` to throw an
// exception, which will terminate the read loop and the ReadFct()
// task
myPort.Close();
}
}
In the above, I have completely ignored the readbtn_Click() method. Lacking a good MCVE, it's not clear what role that button plays in the overall scheme. You seem to have a radio button group (of two buttons) that control whether the port is open or closed. It is not clear why then you have an additional regular button that is seemingly able to also open the port and start reading, independently of the radio group.
If you want that extra button, it seems to me that all it ought to do is change the radio group state, by checking the "open" radio button. Then let the radio group buttons handle the port state and reading. If you need more specific advice as to how to fully integrate my code example above with your entire UI, you will need to provide more detail, preferably in a new question. That new question must include a good MCVE.
I've got a WinForms project that scans a given network and returns valid IP addresses. Once all the addresses are found, I create a user control for each and place it on the form. My functions to ping ip addresses use async and Task which I thought would "wait" to execute before doing something else, but it doesn't. My form shows up blank, then within 5 seconds, all the user controls appear on the form.
Declarations:
private List<string> networkComputers = new List<string>();
Here's the Form_Load event:
private async void MainForm_Load(object sender, EventArgs e)
{
//Load network computers.
await LoadNetworkComputers();
LoadWidgets();
}
The LoadNetworkComputers function is here:
private async Task LoadNetworkComputers()
{
try
{
if (SplashScreenManager.Default == null)
{
SplashScreenManager.ShowForm(this, typeof(LoadingForm), false, true, false);
SplashScreenManager.Default.SetWaitFormCaption("Finding computers");
}
else
Utilities.SetSplashFormText(SplashForm.SplashScreenCommand.SetLabel, "Scanning network for computers. This may take several minutes...");
networkComputers = await GetNetworkComputers();
}
catch (Exception e)
{
MessageBox.Show(e.Message + Environment.NewLine + e.InnerException);
}
finally
{
//Close "loading" window.
SplashScreenManager.CloseForm(false);
}
}
And the last 2 functions:
private async Task<List<string>> GetNetworkComputers()
{
networkComputers.Clear();
List<string> ipAddresses = new List<string>();
List<string> computersFound = new List<string>();
for (int i = StartIPRange; i <= EndIPRange; i++)
ipAddresses.Add(IPBase + i.ToString());
List<PingReply> replies = await PingAsync(ipAddresses);
foreach(var reply in replies)
{
if (reply.Status == IPStatus.Success)
computersFound.Add(reply.Address.ToString());
}
return computersFound;
}
private async Task<List<PingReply>> PingAsync(List<string> theListOfIPs)
{
var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
I'm really stuck on why the form is being displayed before the code in the MainForm_Load event finishes.
EDIT
I forgot to mention that in the LoadNetworkComputers it loads a splash form which lets the user know that the app is running. It's when the form shows up behind that, that I'm trying to avoid. Here's a screenshot (sensitive info has been blacked out):
The reason one would use async-await is to enable callers of functions to continue executing code whenever your function has to wait for something.
The nice thing is that this will keep your UI responsive, even if the awaitable function is not finished. For instance if you would have a button that would LoadNetworkComputers and LoadWidgets you would be glad that during this relatively long action your window would still be repainted.
Since you've defined your Mainform_Loadas async, you've expressed that you want your UI to continue without waiting for the result of LoadNetWorkComputers.
In this interview with Eric Lippert (search in the middle for async-await) async-await is compared with a a cook making dinner. Whenever the cook finds that he has to wait for the bread to toast, he starts looking around to see if he can do something else, and starts doing it. After a while when the bread is toasted he continues preparing the toasted bread.
By keeping the form-load async, your form is able to show itself, and even show an indication that the network computers are being loaded.
An even nicer method would be to create a simple startup-dialog that informs the operator that the program is busy loading network computers. The async form-load of this startup-dialog could do the action and close the form when finished.
public class MyStartupForm
{
public List<string> LoadedNetworkComputers {get; private set;}
private async OnFormLoad()
{
// start doing the things async.
// keep the UI responsive so it can inform the operator
var taskLoadComputers = LoadNetworkComputers();
var taskLoadWidgets = LoadWidgets();
// while loading the Computers and Widgets: inform the operator
// what the program is doing:
this.InformOperator();
// Now I have nothing to do, so let's await for both tasks to complete
await Task.WhenAll(new Task[] {taskLoadComputers, taskLoadWidgets});
// remember the result of loading the network computers:
this.LoadedNetworkComputers = taskLoadComputers.Result;
// Close myself; my creator will continue:
this.Close();
}
}
And your main form:
private void MainForm_Load(object sender, EventArgs e)
{
// show the startup form to load the network computers and the widgets
// while loading the operator is informed
// the form closes itself when done
using (var form = new MyStartupForm())
{
form.ShowDialog(this);
// fetch the loadedNetworkComputers from the form
var loadedNetworkComputers = form.LoadedNetworkComputers;
this.Process(loadedNetworkComputers);
}
}
Now while loading, instead of your mainform the StartupForm is shown while the items are loaded.. The operator is informed why the main form is not showing yet. As soon as loading is finished, the StartupForm closes itself and loading of the main form continues
My form shows up blank, then within 5 seconds, all the user controls appear on the form.
This is by design. When the UI framework asks your app to display a form, it must do so immediately.
To resolve this, you'll need to decide what you want your app to look like while the async work is going on, initialize to that state on startup, and then update the UI when the async work completes. Spinners and loading pages are a common choice.
I am trying to implement a system for closing all modal and non-modal windows in a WPF application (with the exception of the main application window.) When these windows are closed, any code awaiting the result of a dialog should be abandoned.
So far, I've considered/attempted two strategies:
Close and restart the application.
Close all the windows and rely on task cancellation exceptions to abandon all code that was waiting for dialog results. (It bubbles up to the App level and then becomes handled.)
The first solution definitely gets the application to close and will suffice for automatic logout, but I am extremely uncomfortable with code that continues to execute after the dialog it was waiting for has been closed. Is there a nice way to stop the execution of that code?
The second solution has been working relatively well (calling code is aborted) but has one critical flaw: on occasion, some combination of modal and non-modal windows closing in quick succession will cause the application to lock up on a ShowDialog call. (At least, when you pause execution, that's where it ends up.) This is strange because breakpoints clearly demonstrate that the Closed event is being raised on all windows that I intend to close. The result that the end user sees is a login screen that cannot be clicked on but can be tabbed into. So strange! Attempts to dispatch the call at different priorities have been unsuccessful, but a Task.Delay for 100ms might have done the trick. (That's not a real solution, though.)
If each open popup is awaiting a TaskCompletionSource in the background, and, upon completion of the TCS, tries to use the dispatcher to invoke Close on itself, why would one (or more) of the dialogs still be blocking on ShowDialog, even after seeing the Closed event be raised? Is there a way to properly dispatch these calls to Close so they complete successfully? Do I need to be particular about the order in which the windows close?
Some pseudocode-C#-hybrid examples:
class PopupService
{
async Task<bool> ShowModalAsync(...)
{
create TaskCompletionSource, publish event with TCS in payload
await and return the TCS result
}
void ShowModal(...)
{
// method exists for historical purposes. code calling this should
// probably be made async-aware rather than relying on the blocking
// behavior of Window.ShowDialog
create TaskCompletionSource, publish event with TCS in payload
rethrow exceptions that are set on the Task after completion but do not await
}
void CloseAllWindows(...)
{
for every known TaskCompletionSource driving a popup interaction
tcs.TrySetCanceled()
}
}
class MainWindow : Window
{
void ShowModalEventHandler(...)
{
create a new PopupWindow and set the owner, content, etc.
var window = new PopupWindow(...) { ... };
...
window.ShowDialog();
}
}
class PopupWindow : Window
{
void LoadedEventHandler(...)
{
...
Task.Run(async () =>
{
try
await the task completion source
finally
Dispatcher.Invoke(Close, DispatcherPriority.Send);
});
register closing event handlers
...
}
void ClosedEventHandler(...)
{
if(we should do something with the TCS)
try set the TCS result so the popup service caller can continue
}
}
With Window.ShowDialog you create a nested Dispather message loop. With await, it's possible to "jump" on that inner loop and continue the logical execution of an async method there, e.g.:
var dialogTask = window.ShowDialogAsync();
// on the main message loop
await Task.Delay(1000);
// on the nested message loop
// ...
await dialogTask;
// expecting to be back on the main message loop
Now, if dialogTask gets completed via TaskCompletionSource before the corresponding Window.ShowDialog() call returns to the caller, the above code may still end up on the nested message loop, rather than on the main core message loop. E.g., this may happen if TaskCompletionSource.SetResult/TrySetCanceled is called inside the dialog's Window.Closed event handler, or right before/after Window.Close() call. This may create undesired reentrancy side effects, including deadlocks, too.
By looking at your pseudocode, it's hard to tell where the deadlock may be. What is worrying is that you're using Task.Run only to await a task that completes on the main UI thread, or to invoke a synchronous callback on the main UI thread from a pool thread (via Dispatcher.Invoke). You certainly shouldn't need Task.Run here.
I use the following version of ShowDialogAsync for a similar purpose. It makes sure any inner message loops started by nested ShowDialogAsync calls exit before this particular ShowDialogAsync task is complete:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
// testing ShowDialogAsync
async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var modal1 = new Window { Title = "Modal 1" };
modal1.Loaded += async delegate
{
await Task.Delay(1000);
var modal2 = new Window { Title = "Modal 2" };
try
{
await modal2.ShowDialogAsync();
}
catch (OperationCanceledException)
{
Debug.WriteLine("Cancelled: " + modal2.Title);
}
};
await Task.Delay(1000);
// close modal1 in 5s
// this would automatically close modal2
var cts = new CancellationTokenSource(5000);
try
{
await modal1.ShowDialogAsync(cts.Token);
}
catch (OperationCanceledException)
{
Debug.WriteLine("Cancelled: " + modal1.Title);
}
}
}
/// <summary>
/// WindowExt
/// </summary>
public static class WindowExt
{
[ThreadStatic]
static CancellationToken s_currentToken = default(CancellationToken);
public static async Task<bool?> ShowDialogAsync(
this Window #this,
CancellationToken token = default(CancellationToken))
{
token.ThrowIfCancellationRequested();
var previousToken = s_currentToken;
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(previousToken, token))
{
var currentToken = s_currentToken = cts.Token;
try
{
return await #this.Dispatcher.InvokeAsync(() =>
{
using (currentToken.Register(() =>
#this.Close(),
useSynchronizationContext: true))
{
try
{
var result = #this.ShowDialog();
currentToken.ThrowIfCancellationRequested();
return result;
}
finally
{
#this.Close();
}
}
}, DispatcherPriority.Normal, currentToken);
}
finally
{
s_currentToken = previousToken;
}
}
}
}
}
This allows you to cancel the most outer modal window via the associated CancelationToken, and that would automatically close any nested modal windows (those opened with ShowDialogAsync) and exit their corresponding message loops. So, your logical execution flow would end up on the correct outer message loop.
Note it still doesn't guarantee the correct logical order of closing multiple modal windows, if that matters. But it guarantees that the tasks returned by multiple nested ShowDialogAsync calls will get completed in the correct order.
I am not sure that this is gonna fix your issue, but in my case, I created extension methods to help mixing async code and window lifetime management. For example, you can create a ShowDialogAsync() that returns task that will complete when the window is actually closed. A CancellationToken can also be provided to close the dialog automatically if you request a cancellation.
public static class WindowExtension
{
public static Task<bool?> ShowDialogAsync(this Window window, CancellationToken cancellationToken = new CancellationToken())
{
var completionSource = new TaskCompletionSource<bool?>();
window.Dispatcher.BeginInvoke(new Action(() =>
{
var result = window.ShowDialog();
// When dialog is closed, set the result to complete the returned task. If the task is already cancelled, it will be discarded.
completionSource.TrySetResult(result);
}));
if (cancellationToken.CanBeCanceled)
{
// Gets notified when cancellation is requested so that we can close window and cancel the returned task
cancellationToken.Register(() => window.Dispatcher.BeginInvoke(new Action(() =>
{
completionSource.TrySetCanceled();
window.Close();
})));
}
return completionSource.Task;
}
}
In your UI code, you would use the ShowDialogAsync() method like the following. As you can see, when the task is cancelled, the dialog is closed and an OperationCanceledException exception is thrown stopping the flow of your code.
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
YourDialog dialog = new YourDialog();
CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromSeconds(3));
await dialog.ShowDialogAsync(source.Token);
}
catch (OperationCanceledException ex)
{
MessageBox.Show("Operation was cancelled");
}
}
This is JUST for the first part of your issue (closing the windows).
if you do not need any of the results of the windows here is some simple code just to close all but the main window.
This is executing from my main window but you can change the if statement to look for your mainwindow instead if running from alternate area.
foreach(Window item in App.Current.Windows)
{
if(item!=this)
item.Close();
}
As for other threads I am unsure although as mentioned above if you have a list of handles to the threads then you should be able to traverse that as well and kill them.