天天看点

子线程导致 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 作业

/// 发送邮件作业。

public class SendMailJob : IJob

    /// 异步方法。

    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);

        });

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

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

        Action action = new Action(() =>

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

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

        action.BeginInvoke((asyncResult) =>

            Action aciton = asyncResult.AsyncState as Action;

            aciton.EndInvoke(asyncResult);

        }, action);

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

        Thread thread = new Thread(() =>

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

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

        thread.Start();

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

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

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

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

        //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); ;

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

        }).Start();

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

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

1)   没有加 try/catch 的情况

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

2)   加了 try/catch 的情况

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

3)   分析与总结

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

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

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

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

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

继续阅读