1

如何在单独的线程池或专用线程上进行 System.Timer 回调?

背景

我有一个使用 System.Timer 的关键应用程序的轻量级监视器,但无法弄清楚如何使回调不在 ThreadPool 中,并以高优先级运行。

原因如下:

  • 如果关键应用程序没有响应,ThreadPool 将向挂起的线程发送取消令牌、Thread.Abort 或 Win32 Abort(不运行终结器)。

  • 如果应用程序受 CPU 限制,那么我的监控线程应该以最高优先级运行,以防止拒绝对我的显示器提供服务

  • 线程池可能由于线程过多或MaxThreads值过低而耗尽

  • 无法为线程池中的线程设置优先级(AFAIK)

因此,我认为有必要有一个 Timer 并且它在一个单独的线程上回调,尽管我不知道如何实现这一点。

如果这可以通过创建单独的 AppDomain 来实现,CLR 是否仍允许我向不同 AppDomain 中的其他线程发出命令?

4

2 回答 2

1

我想你可以通过Thread.Sleep在专用线程上使用来做到这一点。那是:

void MonitorProc(object state)
{
    while (!ShutdownSignaled)
    {
        Thread.Sleep(MonitorFrequency);
        // Do monitor stuff here
    }
}

如果您将该线程设置为高优先级,它很可能会按预期工作。不过,这不是一个很好的解决方案。

但是,您仍然会遇到一个问题,即您的一个线程可能会崩溃,这会导致整个应用程序瘫痪——包括监视器。

您可能会考虑使监视器成为一个单独的应用程序,该应用程序以高优先级运行,并使用套接字或 WCF 或其他一些通信通道向主应用程序发送命令。这样,如果主应用程序崩溃,监视器就不会死机。您可以编写主应用程序以在启动时自动启动监视器(如果它不存在的话)。

不过,我想知道您列出的条件实际发生的可能性有多大。让我们来看看名单。

我不明白你在这里的第一点,除非是说程序可能会崩溃。在这种情况下,您应该只编写一个单独的监视器应用程序。

如果应用程序受 CPU 限制,如果所有线程都具有相同的优先级,您的监视器线程仍将获得时间片。即使其他线程处于更高的优先级,您的监视器仍然会不时地获得一个时间片。除非线程处于实时优先级。而你不想那样做

如果您的线程用完了,那么您的程序就存在一个根本问题——您可能应该在将其投入生产之前发现这个问题。

进行此监控的最可靠方法是使用单独的应用程序。

于 2012-05-18T00:00:06.367 回答
0

我不知道使用System.Timer. 但是,您可以使用 aSystem.Windows.Forms.Timer来让计时器在您以这种方式创建的特定线程中运行,从而允许您明确设置其优先级。

这是有效的,因为Ticka 的事件System.Windows.Forms.Timer总是在创建它的线程中触发,通常是 UI 线程,但是你可以让一个没有 UI 的线程运行消息循环并实现相同的效果。

看看以下是否可以接受:

static void Main(string[] args)
{
    var monitorContext = new System.Windows.Forms.ApplicationContext();

    var monitor = new Thread(Monitor) { Priority = ThreadPriority.Highest };

    monitor.Start(monitorContext); // Start monitor

    Thread.Sleep(5500); // ...

    monitorContext.ExitThread(); // Terminates monitor
}

public static void Monitor(object context)
{
    var timer = new System.Windows.Forms.Timer()
    {
        Enabled = true,
        Interval = 1000
    };

    timer.Tick += new EventHandler(Monitor_Tick);

    System.Windows.Forms.Application.Run(
        (System.Windows.Forms.ApplicationContext)context);
}

private static void Monitor_Tick(object sender, EventArgs e)
{
    Debug.WriteLine(
        "{0}|{1}", 
        DateTime.UtcNow.ToString(),
        Thread.CurrentThread.Priority);
}

您唯一需要考虑的是,如果在前一个事件仍在处理时触发了一个滴答事件,那么它将被丢弃,因此请确保您在滴答事件中所做的事情不会超过为计时器配置的时间间隔.

于 2012-05-17T22:20:01.480 回答