Reputation: 302
I've seen many questions on how to kill a locked thread, and every answer I've seen says not to kill the thread, don't even use Thread.Abort, just use good programming practice to handle when the thread should stop.
My problem is, I'm stuck using an old 3rd party library I have no control over, and once in awhile this locks up, using pretty much 100% CPU and never letting go. Thread.Abort does not stop this. I can't find any way to stop this.
The best 'workaround' I've found is using a delegate and .AsyncWaitHandle.WaitOne(10000, false);
Code will continue from this point after the timeout, but still whatever thread is running continues to run and use up all of my CPU.
How can I handle this situation? If this 3rd party library locks up, I need a way to kill it.
Thanks for your time, Ben
Upvotes: 3
Views: 1730
Reputation: 6238
I would create a separate application domain to run an assembly that is untrusted or just doesn't work from time to time as in your case. An application domain can be unloaded at any time and it will create a border between your code and external one. Here is a small example. Let's start with the external assembly that contains incorrect code:
public class Processor
{
public void Run()
{
while (true) { /* ... */ }
}
}
Now we need a class that will load BadAssembly.dll, create an instance of Processor and call Run method in the separate thread in order not to block the main thread of your application.
public class Loader : MarshalByRefObject
{
public bool IsCompleted { get; private set; }
public bool IsFaulted { get; private set; }
public void LoadAndRunBadAssembly()
{
var ass = Assembly.LoadFrom("BadAssembly.dll");
var c = (Processor)ass.CreateInstance("BadAssembly.Processor");
Task.Factory.StartNew(() =>
{
try
{
c.Run();
IsCompleted = true;
}
catch (Exception)
{
IsFaulted = true;
}
}, TaskCreationOptions.LongRunning);
}
}
IsCompleted and IsFaulted properties are needed to let us know if an external code has already finished processing or not. Loader is derived from MarshalByRefObject because thanks to that its code will be executed in the separate domain created by us. And here is a code to test Loader class:
var appDomain = AppDomain.CreateDomain("Loader");
try
{
//Here I assume that Loader class is in the same assembly as this code
var loader = (Loader)appDomain.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName,
typeof (Loader).FullName);
loader.LoadAndRunBadAssembly();
Console.WriteLine("Waiting...");
Thread.Sleep(20000); //This simulates waiting for external code
if (loader.IsCompleted)
Console.WriteLine("Processing has finished");
else if (loader.IsFaulted)
Console.WriteLine("There was an error");
else
Console.WriteLine("It lasts too long");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
AppDomain.Unload(appDomain);
}
UPDATE
I've just realized that in order to stop threads in a domain AppDomain.Unload method uses Abort method. It means that you may receive CannotUnloadAppDomainException exception when trying to unload a domain. It will happen because it is not guaranteed that Abort will kill a thread immediately.
Upvotes: 1
Reputation: 5728
If you need a general answer, make a separate process with the code that tends to lock. Kill and restart the process as necessary. With more knowledge of the exact problem you are facing, better solutions may become available.
Upvotes: 5