.net 一段时间后系统线程计时器未启动

8ulbf1ek  于 2023-01-22  发布在  .NET
关注(0)|答案(6)|浏览(122)

我有一个windows服务应用程序。并调试它运行在控制台模式。
这里http://support.microsoft.com/kb/842793写的是Timers.Timer有一个bug,在windows服务中不触发。解决方法是使用Threading.Timer。本文是针对.NET 1.0和1.1的
我使用的是.NET 4,但是过了一段时间线程。计时器也没有启动。那么这是什么原因呢?你有什么建议作为一个变通方案?

1rhkuytd

1rhkuytd1#

您是否在某个地方保留了对计时器的引用,以防止它被垃圾收集?
the docs开始:
只要您在使用Timer,就必须保留对它的引用。与任何托管对象一样,当没有对Timer的引用时,Timer会受到垃圾回收的影响。Timer仍处于活动状态这一事实并不妨碍它被回收。

siv3szwd

siv3szwd2#

计时器对象超出范围,并在一段时间后被垃圾收集器擦除,这会阻止回调的触发。
将对它的引用保存在类的成员中。

b1zrtrql

b1zrtrql3#

变通一下?
个人而言,我建议使用RegisterWaitForSingleObject函数,而不是计时器,这是你遇到的确切原因。RegisterWaitForSingleObject注册了一个委托,在你设置的类似计时器的时间间隔内调用,而且 * 超级 * 容易实现。你可以在几个小时内启动并运行一个测试工具。我在我的Windows服务中使用这种间隔触发方法,这是一个久经考验的、真正稳定的解决方案,对我很有效。
阅读下面的链接,然后转到文章中的链接以获取代码示例和演练。

    • 使用Windows服务在. NET中运行定期进程:**

http://allen-conway-dotnet.blogspot.com/2009/12/running-periodic-process-in-net-using.html

bakd9h0s

bakd9h0s4#

以下是使用RegisterWaitForSingleObject的方法:
当您有代码注册一个计时器时,如下所示:

const int scheduledPeriodMilliseconds = 20000;
new Timer(ServiceBusTimerCallback, parameters, 0, scheduledPeriodMilliseconds);

private static void ServiceBusTimerCallback(object params)
{
}

你可以拥有这个:

const int scheduledPeriodMilliseconds = 20000;
var allTasksWaitHandle = new AutoResetEvent(true);

ThreadPool.RegisterWaitForSingleObject(
    allTasksWaitHandle,
    (s, b) => { ServiceBusTimerCallback(parameters); },
    null,
    scheduledPeriodMilliseconds,
    false);

您可以在ServiceBusTimerCallback之后使用allTasksWaitHandle.Set()通知用户任务已完成,因此它将立即再次运行任务。上面发布的此代码将在timeont过去后运行任务,即每20秒运行一次。
我在WebAPI项目中实现了它,效果很好。

tvmytwxo

tvmytwxo5#

使用企业库进行日志记录和循环线程的windows服务的完整示例。如果不使用企业库,请删除logger.write行

namespace Example.Name.Space
{
public partial class SmsServices : ServiceBase
{
    private static String _state = "";        
    private ManualResetEvent _stop = new ManualResetEvent(false);
    private static RegisteredWaitHandle _registeredWait;
    public WindowsServices()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {                        
        Logger.Write("Starting service", LoggerCategory.Information);            
        _stop.Reset();
        _registeredWait = ThreadPool.RegisterWaitForSingleObject(_stop,
            PeriodicProcess, null, 5000, false);

    }

    protected override void OnStop()
    {            
       // UpdateTimer.Stop();
        _stop.Set();
        Logger.Write("Stopping service", LoggerCategory.Information);
    }        
    private static void PeriodicProcess(object state, bool timedOut)
    {

        if (timedOut)
        {
            // Periodic processing here
            Logger.Write("Asserting thread state", LoggerCategory.Debug);
            lock (_state)
            {
                if (_state.Equals("RUNNING"))
                {
                    Logger.Write("Thread already running", LoggerCategory.Debug);
                    return;
                }
                Logger.Write("Starting thread", LoggerCategory.Debug);
                _state = "RUNNING";
            }
            Logger.Write("Processing all messages", LoggerCategory.Information);
            //Do something 
            lock (_state)
            {
                Logger.Write("Stopping thread", LoggerCategory.Debug);
                _state = "STOPPED";
            }
        }
        else
            // Stop any more events coming along
            _registeredWait.Unregister(null);

    }
    }
}
2q5ifsrm

2q5ifsrm6#

下面是一个实验性的演示,说明一个非根System.Threading.Timer可以进行垃圾回收。仅仅因为它被安排在将来执行,并不能阻止它被回收。

WeakReference timer = StartTimer();
Console.WriteLine($"Before GC.Collect - IsAlive: {timer.IsAlive}");
GC.Collect();
Console.WriteLine($"After GC.Collect  - IsAlive: {timer.IsAlive}");
Thread.Sleep(1000);
Console.WriteLine($"Finished");

static WeakReference StartTimer()
{
    Timer timer = new(_ => Console.WriteLine("Callback!"), null, 500, -1);
    return new(timer);
}

输出:

Before GC.Collect - IsAlive: True
After GC.Collect  - IsAlive: False
Finished

Online demo.

相关问题