统一的线程异常处理

在一个Service程序中, 通常都会有多个Worker线程,它们可能单独运行, 也可能在一个ThreadPool中运行。为了不至于使Worker线程的未处理异常导致主程序的崩溃,我们需要对所有的工作线程以一种一致的方式处理异常,例如通知主线程,然后根据不同的异常做不同的处理,最后优雅地停止该有问题的线程。 例如以下程序:

static void Main(string[] args)
{
    Thread thread1 = new Thread((ThreadStart)Worker_1);
    thread1.Start();

    Thread thread2 = new Thread((ThreadStart)Worker_2);
    thread2.Start();

    thread1.Join();
    thread2.Join();
}

static void Worker_1()
{
    try
    {
        // Do something here.
    }
    catch (Exception e)
    {
        // TODO, handler exception,
        // Notify the main thread and stop this thread gracefully.
    }
}

static void Worker_2()
{
    try
    {
        // Do something here.
    }
    catch (Exception e)
    {
        // TODO, handler exception,
        // Notify the main thread and stop this thread gracefully.
    }
}

在该程序中,我们有 Worker_1 和 Worker_2两个工作线程,它们有相同的异常处理过程。但是问题是,当任务的种类多了起来,如Worker_3, Worker_4, 所有的这样的线程函数都要做相同的异常处理,就导致了不必要的重复,并且很容易遗忘。怎样去除这种重复呢?首先想到的是一个方案是,提供一个辅助函数,它接受一个Action作为参数:

static void SafeThread(Action action)
{
    try
    {
        action();
    }
    catch (Exception e)
    {
        // TODO, handler exception,
        // Notify the main thread and stop this thread gracefully.
    }
}

然后Worker_1 可以这么写:

static void Worker_1()
{
    SafeThread(delegate
    {
        // Do something here.
    });
}

这样是能简化一些。但这种做法会使原来的Worker方法有一个奇怪的包装,而且依然要求我们对每一个Worker做同样的处理。既然Thread的构造函数接受一个 ThreadStart的参数,我们能不能把一个原始的直接的Worker 方法(也是 ThreadStart类型)转换为一个可以处理异常的 ThreadStart 类型呢? 是可以的。首先我们定义这个转换函数如下:

static ThreadStart SafeThread(ThreadStart threadStart)
{
    return () =>
    {
        try
        {
            threadStart();
        }
        catch (Exception e)
        {
            // TODO, handler exception,
            // Notify the main thread and stop this thread gracefully.
        }
    };
}

 

那么我们的Worker线程会很直接:

static void Worker_1()
{
    Console.WriteLine("Worker 1");
    // Do something here.
}

static void Worker_2()
{
    Console.WriteLine("Worker 2");
    // Do something here.
}

而主程序则需要稍加改动,但也非常简单:

static void Main(string[] args)
{
    Thread thread1 = new Thread(SafeThread(Worker_1));
    thread1.Start();

    Thread thread2 = new Thread(SafeThread(Worker_2));
    thread2.Start();

    thread1.Join();
    thread2.Join();
}

 

这对线程函数的编写者来说, 减轻了很多负担, 也不至于会遗漏掉某个线程没被处理。做一次简单的搜索就可以解决问题。

对于接受一个参数的线程(ParameterizedThreadStart)和线程池线程 (WaitCallback),我们又该如何处理呢?

posted @ 2009-12-26 22:48  Ahha  阅读(3178)  评论(15编辑  收藏  举报