views:

3212

answers:

8

The code that I want to write is like this:

void MethodOnThreadA()
{
    for (;;)
    {
        // Do stuff
        if (ErrorConditionMet)
            ThrowOnThread(threadB, new MyException(...));
    }
}

void MethodOnThreadB()
{
    try
    {
        for (;;)
        {
            // Do stuff
        }
    }
    catch (MyException ex)
    {
        // Do the right thing for this exception.
    }
}

I know I can have thread B periodically check, in thread safe way, to see if a flag has been set by thread A, but that makes the code more complicated. Is there a better mechanism that I can use?

Here is a more fleshed out example of periodically checking:

Dictionary<Thread, Exception> exceptionDictionary = new Dictionary<Thread, Exception>();

void ThrowOnThread(Thread thread, Exception ex)
{
    // the exception passed in is going to be handed off to another thread,
    // so it needs to be thread safe.
    lock (exceptionDictionary)
    {
        exceptionDictionary[thread] = ex;
    }
}

void ExceptionCheck()
{
    lock (exceptionDictionary)
    {
        Exception ex;
        if (exceptionDictionary.TryGetValue(Thread.CurrentThread, out ex))
            throw ex;
    }
}

void MethodOnThreadA()
{
    for (;;)
    {
        // Do stuff
        if (ErrorConditionMet)
            ThrowOnThread(threadB, new MyException(...));
    }
}

void MethodOnThreadB()
{
    try
    {
        for (;;)
        {
            // Do stuff
            ExceptionCheck();
        }
    }
    catch (MyException ex)
    {
        // Do the right thing for this exception.
    }
}
+8  A: 

There are enough problems with exceptions that can be thrown on threads by other mechanisms, like aborting threads and the likes, that you should find another way of doing it.

An exception is a mechanism used to signal that a process has experienced something exceptional that it cannot deal with. You should try to avoid writing the code so that an exception is used to signal that something else has experienced something exceptional.

That other thread will most likely not know how to handle the exception in all cases where it could be thrown by your code.

In short, you should find some other mechanism for aborting your threads than using exceptions.

Use event objects or similar to tell a thread to abort its processing, that's the best way.

Lasse V. Karlsen
A: 

I'm interested to know why you would want to do this. There's not an easy way to do it, because it's not a good practice. You should probably go back to your design and figure out a cleaner way to accomplish the end goal.

Eric Z Beard
A: 

I don't think that's a good idea.. Take another crack at this problem - Try using some other mechanism like shared data to signal between threads.

Gishu
A: 

Like the others, I'm not sure that's such a good idea, but if you really want to do it, then you can create a subclass of SynchronizationContext that allows posting and sending delegates to the target thread (if it's a WinForms thread the work is done for you as such a subclass already exists). The target thread will have to implement some sort of a message pump equivalent though, to receive the delegates.

On Freund
+7  A: 

This is NOT a good idea

This article talks about ruby's timeout library. which throws exceptions across threads.

It explains how doing such a thing is fundamentally broken. It's not just broken in ruby, it's broken anywhere that throws exceptions across threads.

In a nutshell, what can (and does) happen is this:

ThreadA:

At some random time, throw an exception on thread B:

ThreadB:

try {
    //do stuff
} finally {
    CloseResourceOne();
    // ThreadA's exception gets thrown NOW, in the middle 
    // of our finally block and resource two NEVER gets closed.
    // Obviously this is BAD, and the only way to stop is to NOT throw
    // exceptions across threads
    CloseResourceTwo();
}

Your 'periodic checking' example is fine, as you're not actually throwing exceptions across threads.
You're just setting a flag which says "throw an exception the next time you look at this flag", which is fine as it doesn't suffer from the "can be thrown in the middle of your catch or finally block" problem.
However, if you're going to do that, you may as well just be setting an "exitnow" flag, and using that and save yourself the hassle of creating the exception object. A volatile bool will work just fine for that.

Orion Edwards
A: 

@Orion Edwards

I take your point about an exception being thrown in the finally block.

However, I think there is a way - using yet another thread - of using this exception-as-interrupt idea.

Thread A:

At some random time, throw an exception on thread C:

Thread B:

try {
    Signal thread C that exceptions may be thrown
    //do stuff, without needing to check exit conditions
    Signal thread C that exceptions may no longer be thrown
}
catch {
    // exception/interrupt occurred handle...
}
finally {
    // ...and clean up
    CloseResourceOne();
    CloseResourceTwo();
}

Thread C:

 while(thread-B-wants-exceptions) {
        try {
            Thread.Sleep(1) 
        }
        catch {
            // exception was thrown...
            if Thread B still wants to handle exceptions
                throw-in-B
        }
    }

Or is that just silly?

Rob
+1  A: 

While researching another issue, I came across this article which reminded me of your question:

Plumbing the Depths of the ThreadAbortException using Rotor

It shows the gyrations that .NET goes through to implement Thread.Abort() -- presumably any other cross-thread exception would have to be similar. (Yeech!)

Eric
A: 

What Orion Edwards is saying is not entirely true: is not the "only" way.

// Obviously this is BAD, and the only way to stop is to NOT throw
// exceptions across threads

Using CER (Constrained Execution Regions) in C# allows you to release your resources as an atomic operation, protecting your code from inter-thread exceptions. This technique is used by several classes of the .NET Framework which work with Windows' native API, where an unreleased handle may cause a memory leak.

See http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions.aspx

The following example shows how to reliably set handles by using the PrepareConstrainedRegions method. To reliably set a handle to a specified pre-existing handle, you must ensure that the allocation of the native handle and the subsequent recording of that handle within a SafeHandle object is atomic. Any failure between these operations (such as a thread abort or out-of-memory exception) will result in the native handle being leaked. You can use the PrepareConstrainedRegions method to make sure that the handle is not leaked.

As simple as:

public MySafeHandle AllocateHandle()
{
    // Allocate SafeHandle first to avoid failure later.
    MySafeHandle sh = new MySafeHandle();

    RuntimeHelpers.PrepareConstrainedRegions();
    try { }
    finally  // this finally block is atomic an uninterruptible by inter-thread exceptions
    {
        MyStruct myStruct = new MyStruct();
        NativeAllocateHandle(ref myStruct);
        sh.SetHandle(myStruct.m_outputHandle);
    }

    return sh;
}
GenericProgrammer