最近的开发用到了多线程,在找资料的时候看到《Ahha, programming!》博客上的这篇文章,觉得思路不错,特此转载。
文章名:统一的线程异常处理
出处:http://www.cnblogs.com/oujinliang/archive/2009/12/26/1633041.html
原文如下:
-------------------------------------------------
统一的线程异常处理
在 一个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),我们又该如何处理呢?
Thread thread1 = new Thread(SafeThread(Worker_1));
如果不看 SafeThread 方法的定义,这样写无疑会以为错的,因为Thread 构造器里应该放一个无参的方法。
居然被楼主发明这种写法,感觉上不是很好
受教了。
===============================================
看到这句话我就知道多个方法可能要由1个ThreadStart来代理,如果方法有参数需要传递,那LZ的这种方法是否是线程安全呢
包装 Thread 中我们需要使用的东西,比如 Start(),Abort()等,实现自定义的线程,并捕捉异常,进行统一控制,不是更好?
你是说象这样?
01 | public class MyThread |
02 | { |
03 | private Thread thread; |
04 | public MyThread(ThreadStart start) |
05 | { |
06 | this .thread = new Thread(start); |
07 | } |
08 |
09 | public void Start() |
10 | { |
11 | try |
12 | { |
13 | this .thread.Start(); |
14 | } |
15 | catch (Exception e) |
16 | { |
17 | Console.WriteLine(e); |
18 | } |
19 | } |
20 |
21 | public void Join() |
22 | { |
23 | this .thread.Join(); |
24 | } |
25 | } |
这样是不行的,线程的异常不是仅仅通过 try/catch Start 就可以解决的。
况且我认为,既然.NET 提供了一整套线程机制,我们就利用现有的机制就好了,包装Thread的各种方法引入的复杂性我觉得有点大了。
线程安全是个有趣的话题,我在这里没有涉及。不过我觉得各个 Worker线程有责任负责其引用的变量的线程安全问题。
至于带参数的线程转换,同上:
01 | static ParameterizedThreadStart SafeThread(ParameterizedThreadStart threadStart) |
02 | { |
03 | return (obj) => |
04 | { |
05 | try |
06 | { |
07 | threadStart(obj); |
08 | } |
09 | catch (Exception e) |
10 | { |
11 | // TODO, handler exception, |
12 | // Notify the main thread and stop this thread gracefully. |
13 | } |
14 | }; |
15 | } |
或者直接调用前面的无参线程转换函数:
1 | static ParameterizedThreadStart SafeThread(ParameterizedThreadStart threadStart) |
2 | { |
3 | return (obj) => SafeThread(() => threadStart(obj))(); |
4 | } |
我觉得仅仅为了exception 去封装的确没必要,
但如果想更深入理解,就应该向虎山行。
实际上OS通过 SEH 来管理 Exception,只是我们在上层无法看到此种内容,编写的代码表面是简单了,实际上时一样的。
可以继续探讨...
{
try
{
this.thread.Start();
}
// 此类异常需要单独处理
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
同时,还需要考虑多线程的问题。^_^
让使用者编码简单实际也是封装的一个主要目的。至于采用什么形式那就是其利益和弊端的一个平衡了。
线程的封装是一个比较有意思的话题,孤剑兄是否有兴趣写一篇这样的文章呢?
http://kb.cnblogs.com/page/42528/