views:

179

answers:

5

Hello! In MS Exam 70-536 .Net Foundation, Chapter 7 "Threading" in Lesson 1 Creating Threads there is a text:

Be aware that because the WorkWithParameter method takes an object, Thread.Start could be called with any object instead of the string it expects. Being careful in choosing your starting method for a thread to deal with unknown types is crucial to good threading code. Instead of blindly casting the method parameter into our string, it is a better practice to test the type of the object, as shown in the following example:

' VB
Dim info As String = o as String
If info Is Nothing Then
    Throw InvalidProgramException("Parameter for thread must be a string")
End If
// C#
string info = o as string;
if (info == null)
{
    throw InvalidProgramException("Parameter for thread must be a string");
} 

So, I've tried this but exception is not handled properly (no console exception entry, program is terminated), what is wrong with my code (below)?

class Program
    {
        static void Main(string[] args)
        {
            Thread thread = new Thread(SomeWork);
            try
            {
                thread.Start(null);
                thread.Join();
            }
            catch (InvalidProgramException ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {

                Console.ReadKey();
            }
        }

        private static void SomeWork(Object o)
        {
            String value = (String)o;
            if (value == null)
            {
                throw new InvalidProgramException("Parameter for "+
                    "thread must be a string");
            }
        }
    }

Thanks for your time!

+1  A: 

Exceptions don't traverse threads, it simply shuts down your executing code. In other words, you will never catch an exception thrown on a separate thread.

Otávio Décio
its working, with fix Andrey pointed out
Max Gontar
+1  A: 

An unhandled exception on a background thread will terminate your application. This is a behaviroal change in .NET 2.0. If you don't want your application to die when that happens, you should wrap the code in a try catch and log that exception:

private static void SomeWork(Object o)
{
    try
    {
        // execution here
    }
    catch (Exception ex)
    {
        Logger.Log(ex);
    }
}
Steven
thanks, good suggestion, but I would really like to pass it out the thread
Max Gontar
+1  A: 

First start in VS in debug mode. Now to code, several issues:

    catch (InvalidProgramException ex)
    {
        Console.WriteLine(ex.Message);
    }

will never be executed because exception is local to thread. If you throw exception in spawned thread other threads will not see it.

throw new InvalidProgramException("Parameter for "+
                    "thread must be a string");

this line causes unhandled exception, because in that thread no one caught it. unhandled exception fail entire app.

    String value = (String)o;
    if (value == null)

o can be null which is valid value for String and your code will throw exceptions if o is not string. Did you meant:

    String value = o as String;
    if (value == null)
Andrey
thanks, my mistake
Max Gontar
please accept if i was right :)
Andrey
+3  A: 

The exception handler in your Main method is running in a different thread than the exception is thrown from. So, the exception in thread cannot be caught in Main. Check here for a discussion of why you don't want to throw/catch exceptions across threads. What you should do instead is use an object that will wrap your thread logic but support exceptions. E.g.:

class Program
{
    static void Main(string[] args)
    {
        ExceptionHandlingThreadWrapper wrapper = new ExceptionHandlingThreadWrapper();
        Thread thread = new Thread(wrapper.Run);
        try
        {
            thread.Start(null);
            thread.Join();
            if (wrapper.Exception != null)
                throw new Exception("Caught exception", wrapper.Exception);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
        finally { Console.ReadKey(); }
    }
}

public class ExceptionHandlingThreadWrapper
{
    public ExceptionHandlingThreadWrapper()
    {
        this.Exception = null;
    }

    public Exception Exception { get; set; }

    public void Run(Object obj)
    {
        try
        {
            String value = obj as String;
            if (value == null)
                throw new Exception("Argument must be string");
        }
        catch (Exception ex)
        {
            this.Exception = ex;
        }
    }
}
Dathan
A: 

Have you considered using BeginInvoke and EndInvoke instead of creating your own Thread? This will allow the exception to be caught and brought back to the calling method.

class Program
{
    private delegate void WorkDelegate(Object o);

    static void Main(string[] args)
    {
        WorkDelegate workDel = new WorkDelegate(SomeWork);
        // first argument is passing a null object that will throw 
        // the exception in the SomeWork method
        IAsyncResult result = workDel.BeginInvoke(null, null, null);
        try
        {
            workDel.EndInvoke(result);
        }
        catch (InvalidProgramException ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            Console.ReadKey();
        }
    }

    private static void SomeWork(Object o)
    {
        String value = o as String;
        if (value == null)
        {
            throw new InvalidProgramException("Parameter for "+
                "thread must be a string");
        }
    }
}
Randal