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

时间:2009-11-19 17:04:17

标签: c# .net windows-services

我正在维护一些看起来像这样的代码。这是一项Windows服务,每隔30分钟就可以完成一些工作。 ActualWorkDoneHere方法运行大约需要30秒,但如果在运行时它停止,则会使事情处于错误状态。防止这种情况发生的最佳方法是什么?我应该用onstop方法中设置为false的布尔值替换While(true)(删除线程Abort调用)?有没有办法判断线程是否正在休眠?

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 个答案:

答案 0 :(得分:22)

当我有这样的事情时,我通常使用ManualResetEvent。这是在Stop()电话中设置的。然后我等待超时:

for (;;)
{
    if (_stop.WaitOne(timeout))
        break;
    DoSomething();
}

答案 1 :(得分:2)

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

而不是Thread.Sleep你可以例如睡眠500毫秒并检查中止标志是否仍然是假,在30分钟之前再睡500毫秒等,然后完成工作等等(这将是一种实用的方法)。如果你想要更优雅的东西,你可以使用带有超时的ManualResetEvent来等待主线程发出中止它的时间。

答案 2 :(得分:2)

哇大家都这么复杂。使用计时器:

比赛: 最初的帖子在OnStop上有一个已经修复的比赛。据我所知,将服务置于停止状态不会中止用于服务定时器的线程池线程。定时器触发和同时停止服务的条件无关紧要。 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();
        }
    }
}

答案 3 :(得分:1)

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

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

然后在OnStart中,你做这样的事情(我有一个帮助方法,在这个例子中做一些记录,“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;
        }
    }

答案 4 :(得分:1)

您可以使用锁定对象来防止在您的工作实际发生时停止线程......

    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会报告该服务没有停止。

答案 5 :(得分:0)

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

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

            }
        }

    }
}

干杯, 巴拉斯。

答案 6 :(得分:0)

我的服务侦听网络套接字,所以我所做的是创建一对连接的网络套接字,并使用select系统调用来监听两者。如果联合对报告准备好阅读,我知道要关闭该服务。

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

答案 7 :(得分:0)

while (true)
        {
            if (m_reset.WaitOne(1,false))
                break;
             // DoSomething


        }

请在onStop()

中试试