代码之家  ›  专栏  ›  技术社区  ›  Stimy

如何安全地停止在Windows服务中运行的C.NET线程?

  •  14
  • Stimy  · 技术社区  · 15 年前

    我正在维护一些看起来像这样的代码。这是一个Windows服务,每30分钟工作一次。actualWorkDonehere方法运行大约需要30秒,但如果在运行时停止,可能会使事情处于糟糕的状态。防止这种情况发生的最佳方法是什么?我应该将while(true)替换为在onstop方法中设置为false的布尔值吗(删除线程中止调用)?有什么方法可以判断线程是否正在休眠吗?

    namespace WorkService
    {
        public partial class WorkService : ServiceBase
        {
            private Thread _workerThread = null;
    
            public WorkService()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                _workerThread = new Thread(new ThreadStart(DoWork));
                _workerThread.Start();
            }
    
            protected override void OnStop()
            {
                _workerThread.Abort();
            }
    
            static void DoWork()
            {
                int sleepMinutes = 30;
    
                while (true)
                {
                     ActualWorkDoneHere();
    
                     System.Threading.Thread.Sleep(new TimeSpan(0, sleepMinutes, 0));
                }
            }
        }
    }
    
    8 回复  |  直到 6 年前
        1
  •  22
  •   Roger Lipscombe    15 年前

    当我有这样的东西时,我通常用 ManualResetEvent . 这是在 Stop() 打电话。然后我超时等待:

    for (;;)
    {
        if (_stop.WaitOne(timeout))
            break;
        DoSomething();
    }
    
        2
  •  2
  •   Grzenio    15 年前

    自己实施是唯一安全的选择。即使您找到了一种方法来查明线程是否正在休眠,但如果您试图终止线程,您仍然会有一个争用条件(因为它可能会在检查之后和终止线程之前开始处理)。

    代替thread.sleep,你可以睡500毫秒,在30分钟前检查abort标志是否仍然是假的,再睡500毫秒等,然后再做这个工作,等等(这是一个实用的方法)。如果您想要更优雅的东西,可以使用带有超时的manualResetevent来等待主线程发出终止时间的信号。

        3
  •  2
  •   JeffreyABecker    15 年前

    哇,每个人都让事情变得如此复杂。使用计时器:

    关于种族: 原来的岗位有一场比赛,已经被修正。据我所知,将服务置于停止状态不会中止用于服务计时器的线程池线程。定时器触发和服务同时停止的情况是无关的。actualWorkDoneHere()将运行或不运行。两者都是可接受的条件。

    namespace WorkService
    {
        public partial class WorkService : ServiceBase
        {
            protected const int sleepMinutes = 30;
            protected System.Timers.Timer _interval;
            protected bool _running = false;
    
            public WorkService()
            {
                InitializeComponent();
                _interval = new System.Timers.Timer();
                _interval.Elapsed += new ElapsedEventHandler(OnTimedEvent);
                _interval.Interval = sleepMinutes * 60 * 1000;
                _running = false;
            }
    
            protected override void OnStart(string[] args)
            {
                _running = true;
                _interval.Enabled = true;
            }
    
            protected override void OnStop()
            {
                _interval.Enabled = false;
                _running = false;
            }
    
            private static void OnTimedEvent(object source, ElapsedEventArgs e)
            {
                if(_running)
                    ActualWorkDoneHere();
            }
        }
    }
    
        4
  •  1
  •   dcp    15 年前

    这是一种方法。将以下变量添加到类中:

    private readonly object syncObject = new object();
    private bool stopping;
    private bool stopped = true;
    

    然后在OnStart中,您可以这样做(我有一个helper方法,它在这个示例中进行一些日志记录,“run”方法完成实际工作):

        public override void OnStart()
        {
            while (stopping)
            {
                Thread.Sleep(MSECS_SLEEP_FOR_STOP);
            }
    
            lock (syncObject)
            {
                // make sure task isn't already started
                if (!stopped)
                {
                    Helper.WriteToLog(logger, Level.INFO,
                        string.Format("{0} {1}", TASK_NAME, "is already started."));
                    return;
                }
                stopped = false;
            }
    
            // start task in new thread
            Thread thread = new Thread(Run);
            thread.Start();
    
            Helper.WriteToLog(logger, Level.INFO,
                string.Format("{0} {1}", TASK_NAME, "was started."));
        }
    

    执行线程工作的“run”方法如下(processInterval是两次运行之间等待的时间,可以在构造函数中设置它,也可以对其进行硬编码):

        private void Run()
        {
            try
            {
                while (!stopping)
                {
                    // do work here
    
                    // wait for process interval
                    DateTime waitStart = DateTime.Now;
                    while (((DateTime.Now - waitStart).TotalMilliseconds < processInterval) && !stopping)
                    {
                        // give processing time to other threads
                        Thread.Sleep(MSECS_SLEEP_FOR_CHECK);
                    }
                }
                lock (syncObject)
                {
                    stopped = true;
                    stopping = false;
                }
    
                Helper.WriteToLog(logger, Level.INFO,
                    string.Format("{0} {1}", TASK_NAME, "was stopped."));
            }
            catch (Exception e)
            {
                // log the exception, but ignore it (i.e. don't throw it)
                Helper.LogException(logger, MethodBase.GetCurrentMethod(), e);
            }
        }
    

    然后在OnStop中,您将执行以下操作:

        public override void OnStop()
        {
            lock (syncObject)
            {
                if (stopping || stopped)
                {
                    Helper.WriteToLog(logger, Level.INFO,
                        string.Format("{0} {1}", TASK_NAME, "is already stopped."));
                    return;
                }
                stopping = true;
            }
        }
    
        5
  •  1
  •   Richard    15 年前

    您可以使用一个锁对象来防止线程在您的工作实际发生时被停止…

        private static readonly object _syncRoot = new object();
    
        protected override void OnStop()
        {
            lock (_syncRoot) 
            {
                _workerThread.Abort();
            }
        }
    
        static void DoWork()
        {
            int sleepMinutes = 30;
    
            while (true)
            {
                 lock (_syncRoot) 
                 {
                     ActualWorkDoneHere();
                 }
    
                 System.Threading.Thread.Sleep(new TimeSpan(0, sleepMinutes, 0));
            }
        }
    

    但是你应该小心-如果你的 ActualWorkDoneHere() 函数花费的时间太长,Windows将报告服务无法停止。

        6
  •  0
  •   Fredrik Mörk    15 年前

    尝试使用自动设置标志来处理服务的停止。在这种情况下,您不必执行线程中止。已在下面添加示例代码

    namespace WorkService
    {
        public partial class WorkService : ServiceBase
        {
        AutoResetEvent serviceStopEvent = new AutoResetEvent( false);
    
            public WorkService()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                Thread workerThread = new Thread(new ThreadStart(DoWork));
                workerThread.Start();
            }
    
            protected override void OnStop()
            {
               serviceStopEvent.Set();
            }
    
            static void DoWork()
            {
                int sleepMinutes = 30;
            WaitHandle[ ] handles = new WaitHandle[ ] { serviceStopEvent };
    
                while (WaitHandle.WaitAny( handles))
                {
                     ActualWorkDoneHere();
    
                }
            }
    
        }
    }
    

    干杯, 巴拉斯。

        7
  •  0
  •   Joshua    15 年前

    我的服务在网络套接字上监听,所以我所做的是创建一对连接的网络套接字,并使用SELECT系统调用来侦听这两个网络套接字。如果加入的一对报告准备好阅读,我就知道要关闭服务。

    这个技巧可以用来触发任意数量的线程关闭,只要它们没有从连接的对中读取。

        8
  •  0
  •   Robin Gupta    6 年前
    while (true)
            {
                if (m_reset.WaitOne(1,false))
                    break;
                 // DoSomething
    
    
            }
    

    请在里面试试这个 onStop()