统一的线程异常处理(转载)

最近的开发用到了多线程,在找资料的时候看到《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),我们又该如何处理呢?

2
0
(请您对文章做出评价)
« 上一篇: The only valid measurement of code quality?
» 下一篇: 善用 C# 3.0 Extensions 方法 -- 以及常用辅助方法集
posted @ 2009-12-26 22:48 Ahha 阅读(1086) 评论(15)   编辑 收藏 网摘

评论
   回复   引用    
#1楼 58.24.84.* 2009-12-27 00:06 | asdfffff111[未注册用户]
lz这种用返回匿名委托的方式确实挺奇怪的。

Thread thread1 = new Thread(SafeThread(Worker_1));

如果不看 SafeThread 方法的定义,这样写无疑会以为错的,因为Thread 构造器里应该放一个无参的方法。

居然被楼主发明这种写法,感觉上不是很好

   回复   引用    
#2楼 218.240.33.* 2009-12-27 00:12 | gxq[未注册用户]
嘿嘿.沙发!!!
受教了。

   回复   引用   查看    
#3楼 2009-12-27 00:19 | 孤剑       
既然是线程的,为何不自定义一个线程了,重写 Start(); 包装一下,加一个 try...catch 即可。

   回复   引用   查看    
#4楼 [楼主]2009-12-27 00:27 | Ahha       
@孤剑
Thread 是一个 sealed class, 不能继承。 如果包装,恐怕包装的东西会比较多。

   回复   引用   查看    
#5楼 2009-12-27 02:05 | shenzhen       
楼主有创意,还真没想过可以这样简化代码。。。谢谢啦!
   回复   引用   查看    
#6楼 2009-12-27 12:54 | Terry Sun       
"对所有的工作线程以一种一致的方式处理异常,例如通知主线程"
===============================================
看到这句话我就知道多个方法可能要由1个ThreadStart来代理,如果方法有参数需要传递,那LZ的这种方法是否是线程安全呢

   回复   引用   查看    
#7楼 2009-12-27 14:47 | 玉开       
不错哟
   回复   引用   查看    
#8楼 2009-12-27 17:01 | 孤剑       
to Ahha:
包装 Thread 中我们需要使用的东西,比如 Start(),Abort()等,实现自定义的线程,并捕捉异常,进行统一控制,不是更好?

   回复   引用   查看    
#9楼 [楼主]2009-12-27 17:59 | Ahha       
@孤剑
你是说象这样?
01public 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的各种方法引入的复杂性我觉得有点大了。
   回复   引用   查看    
#10楼 [楼主]2009-12-27 18:09 | Ahha       
@Terry Sun
线程安全是个有趣的话题,我在这里没有涉及。不过我觉得各个 Worker线程有责任负责其引用的变量的线程安全问题。

至于带参数的线程转换,同上:
01static 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}


或者直接调用前面的无参线程转换函数:
1static ParameterizedThreadStart SafeThread(ParameterizedThreadStart threadStart)
2{
3    return (obj) => SafeThread(() => threadStart(obj))();
4}

   回复   引用   查看    
#11楼 2009-12-27 23:14 | 葡萄园de杂烩       
有什么情况, 需要在不同地方创建有多个线程,而且线程的异常处理机制会一样?
   回复   引用   查看    
#13楼 2009-12-28 22:54 | 孤剑       
是的,代码很详细,的确自己控制需要考虑一些信息。
我觉得仅仅为了exception 去封装的确没必要,
但如果想更深入理解,就应该向虎山行。
实际上OS通过 SEH 来管理 Exception,只是我们在上层无法看到此种内容,编写的代码表面是简单了,实际上时一样的。

可以继续探讨...

   回复   引用   查看    
#14楼 2009-12-28 22:57 | 孤剑       
public void Start()
{
try
{
this.thread.Start();
}
// 此类异常需要单独处理
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}

同时,还需要考虑多线程的问题。^_^

   回复   引用   查看    
#15楼 [楼主]2009-12-29 12:43 | Ahha       
@孤剑
让使用者编码简单实际也是封装的一个主要目的。至于采用什么形式那就是其利益和弊端的一个平衡了。

线程的封装是一个比较有意思的话题,孤剑兄是否有兴趣写一篇这样的文章呢?


   回复   引用   查看    
#16楼 2009-12-30 23:49 | 孤剑       
嘿嘿,相关内容园内的兄弟们已经写了,
http://kb.cnblogs.com/page/42528/

 

转载于:https://www.cnblogs.com/nliao/archive/2009/12/31/1637119.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值