zoukankan      html  css  js  c++  java
  • 子线程导致 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 时)委托异步调用的线程同样会停止服务

  • 相关阅读:
    Android “swipe” vs “fling”
    Parcel
    ViewConfiguration 和 ViewConfigurationCompat
    Hive记录-配置支持事务管理hive-site.xml
    SQL记录-资源正忙online或nowait
    SQL记录-解锁和dbms_job操作
    Java记录-SpringMVC整合Echarts画地图加散点图
    Hive记录-impala常用命令
    Hive记录-Sqoop常用命令
    Linux记录-shell一行代码杀死进程(收藏)
  • 原文地址:https://www.cnblogs.com/abeam/p/8748729.html
Copyright © 2011-2022 走看看