子线程导致 Windows 服务停止的情况(Topshelf 结合 Quartz.NET)

Ø  前言

本文主要记录子线程导致 Topshelf Quartz.NET Windows 服务停止的现象,以及使用几种常用子线程的注意事项。因为我们有时可能需要开启多个线程执行复杂的逻辑,如果某个子线发生了异常就导致服务停止了,那还怎么愉快的玩耍?!

 

1.   还是以之前使用 Quartz.NET 实现作业串行执行为例,我们模拟在发送短信发送邮件中发生异常的情况,代码如下:

1)   首先修改 SendSMSJob 作业

/// <summary>

/// 发送短信作业。

/// </summary>

public class SendSMSJob : IJob

{

    /// <summary>

    /// 作业被触发时执行该方法。

    /// </summary>

    public void Execute(IJobExecutionContext context)

    {

        Log.Logger.InfoFormat("开始执行发送短信作业,线程Id为:{0}", Thread.CurrentThread.ManagedThreadId);

        int i = 10, j = 0;

        int r = i / j;

        Log.Logger.InfoFormat("计算结果:{0} / {1} = {2}", i, j, r);

        Log.Logger.Info("发送短信作业执行结束");

    }

}

 

2)   然后再修改 SendMailJob 作业

/// <summary>

/// 发送邮件作业。

/// </summary>

public class SendMailJob : IJob

{

    /// <summary>

    /// 异步方法。

    /// </summary>

    public async void AsyncMethod1()

    {

        await Task.Run(() =>

        {

            try

            {

                Log.Logger.InfoFormat("开始执行(异步方法),线程为:{0}", Thread.CurrentThread.ManagedThreadId); ;

                string str = ((object)null).GetType().Name;

                Log.Logger.InfoFormat("结束执行(异步方法),结果:{0}", str);

            }

            catch (Exception ex)

            {

                Log.Logger.ErrorFormat("发送邮件作业发生异常:{0}", ex.Message);

            }

        });

    }

 

    /// <summary>

    /// 作业被触发时执行该方法。

    /// </summary>

    public void Execute(IJobExecutionContext context)

    {

        Log.Logger.InfoFormat("开始执行发送邮件作业,线程Id为:{0}", Thread.CurrentThread.ManagedThreadId);

 

        //1. 使用委托异步调用的线程

        Action action = new Action(() =>

        {

            try

            {

                Log.Logger.InfoFormat("开始执行(委托异步),线程为:{0}", Thread.CurrentThread.ManagedThreadId); ;

                string str = ((object)null).GetType().Name;

                Log.Logger.InfoFormat("结束执行(委托异步),结果:{0}", str);

            }

            catch (Exception ex)

            {

                Log.Logger.ErrorFormat("发送邮件作业发生异常:{0}", ex.Message);

            }

        });

        action.BeginInvoke((asyncResult) =>

        {

            Action aciton = asyncResult.AsyncState as Action;

            aciton.EndInvoke(asyncResult);

        }, action);

 

        //2. 使用开启的新线程

        Thread thread = new Thread(() =>

        {

            try

            {

                Log.Logger.InfoFormat("开始执行(开启新线程),线程为:{0}", Thread.CurrentThread.ManagedThreadId); ;

                string str = ((object)null).GetType().Name;

                Log.Logger.InfoFormat("结束执行(开启新线程),结果:{0}", str);

            }

            catch (Exception ex)

            {

                Log.Logger.ErrorFormat("发送邮件作业发生异常:{0}", ex.Message);

            }

        });

        thread.Start();

 

        //3. 使用线程池中的线程

        System.Threading.ThreadPool.QueueUserWorkItem((state) =>

        {

            try

            {

                Log.Logger.InfoFormat("开始执行(线程池),线程为:{0}", Thread.CurrentThread.ManagedThreadId); ;

                string str = ((object)null).GetType().Name;

                Log.Logger.InfoFormat("结束执行(线程池),结果:{0}", str);

            }

            catch (Exception ex)

            {

                Log.Logger.ErrorFormat("发送邮件作业发生异常:{0}", ex.Message);

            }

        });

 

        //4. 使用异步方法中的线程(以 System.Threading.Tasks.Task.Run() 方式)

        AsyncMethod1();

 

        //5. 使用异步任务中的线程(以 System.Threading.Tasks.Task.Run() 方式)

        System.Threading.Tasks.Task.Run(() =>

        {

            Log.Logger.InfoFormat("开始执行(Task.Run()),线程为:{0}", Thread.CurrentThread.ManagedThreadId); ;

            string str = ((object)null).GetType().Name;

            Log.Logger.InfoFormat("结束执行(Task.Run()),结果:{0}", str);

        });

 

        //6. 使用异步任务中的线程(以 System.Threading.Tasks.Task.Factory.StartNew() 方式)

        System.Threading.Tasks.Task.Factory.StartNew(() =>

        {

            Log.Logger.InfoFormat("开始执行(Task.Factory.StartNew()),线程为:{0}", Thread.CurrentThread.ManagedThreadId); ;

            string str = ((object)null).GetType().Name;

            Log.Logger.InfoFormat("结束执行(Task.Factory.StartNew()),结果:{0}", str);

        }).Start();

 

        Log.Logger.Info("发送邮件作业执行结束");

    }

}

 

2.   分别运行 SendMailJob 作业的几种方式(运行时注释其他 5 种方式)

1)   没有加 try/catch 的情况

clip_image002[4]

clip_image004[4]

 

2)   加了 try/catch 的情况

clip_image006[4]

 

3)   分析与总结

1.   首先主线程(就是 windows 服务主动开启的线程)发生异常时,终止当前线程执行,但不会停止服务。

2.   【推荐】异步任务(Task)开启的线程发生异常时,也是终止当前线程执行,但不会停止服务。

3.   使用(未加 try/catch 时)委托异步调用的线程、开启的新线程、线程池中的线程、异步方法中的线程,发生异常时,会停止服务

4.   使用(加 try/catch 时)开启的新线程、线程池中的线程、异步方法中的线程,发生异常时,不会停止服务

5.   使用(加 try/catch 时)委托异步调用的线程同样会停止服务

posted @ 2018-04-08 23:24  Abeam  阅读(382)  评论(0编辑  收藏  举报