0

我在我的多线程应用程序中使用了一些System.Threading.Timer(s)(实际上是三个这样的计时器),但是,尽管它工作得很好,但我感觉我做错了并且消耗了不必要的资源。实际上,我正在做的是:

  1. 在我的应用程序的主窗口(WPF 窗口,但不要认为这很重要)我打开了三个线程。

  2. 在这三个线程的每一个中,我初始化并启动三个计时器之一

我使用三个不同的线程来执行此操作的原因是因为我记得我在某处读过(找不到在哪里),当我启动时System.Threading.Timer我不能再使用当前线程(就像线程会以某种方式停止在定时器开始)。

现在,我测试了下一个简单的应用程序,似乎 Timer 和启动它的线程之间没有冲突。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    System.Threading.Timer _t1, _t2, _t3;

    private void Form1_Load(object sender, EventArgs e)
    {
        _t1 = new System.Threading.Timer(new TimerCallback(_t1Run),
                           null, TimeSpan.Zero, TimeSpan.FromMilliseconds(15));
        _t2 = new System.Threading.Timer(new TimerCallback(_t2Run),
               null, TimeSpan.Zero, TimeSpan.FromMilliseconds(35));
        _t3 = new System.Threading.Timer(new TimerCallback(_t3Run),
               null, TimeSpan.Zero, TimeSpan.FromMilliseconds(150));

        for (int i = 1; i <= 5000; i++)
        {
            Console.WriteLine("Writting on main form " + i);
        }
    }

    void _t1Run(object State)
    {
        _t1.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
        Console.WriteLine("running 1");
        _t1.Change(TimeSpan.FromMilliseconds(15), TimeSpan.FromMilliseconds(15));
    }

    void _t2Run(object State)
    {
        _t2.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
        Console.WriteLine("running 2");
        _t2.Change(TimeSpan.FromMilliseconds(35), TimeSpan.FromMilliseconds(35));
    }

    void _t3Run(object State)
    {
        _t3.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
        Console.WriteLine("running 3");
        _t3.Change(TimeSpan.FromMilliseconds(150), TimeSpan.FromMilliseconds(150));
    }

}

那么,我可以在主窗口上启动所有三个计时器(或者我需要单独的线程)吗?有人可以确认这不会导致任何问题(因为在我测试过的应用程序中它似乎没有任何问题)?

编辑:这三个定时器有不同的用途,其中两个实际上有一些 UI 交互(一个与主窗口交互,另一个与辅助窗口交互 - 第三个只做一些后台操作),但我不这样做的原因用途System.Windows.Forms.Timer是计时器进行很多操作,如果我在主线程上执行所有这些操作,那么我实际上会阻止所有 GUI 交互(我以 100-200 毫秒的间隔调用这些计时器线程,所以只是想象 ...) 。这是我如何从计时器调用的线程进行 GUI 交互的示例:

MainWindow.ThisInstance.TitleLabel.Dispatcher.BeginInvoke((Action)(() => UpdateMainWindow_TimeLabel(TimeLabelValue)));
4

1 回答 1

4

如果在 Timer 回调代码中有某种 UI 元素交互,则应该使用System.Windows.Forms.Timer而不是。System.Threading.Timer

每当时间跨度过去时,System.Threading.Timer 就会产生一个线程池线程。但这可能会给 Form 元素带来问题,因为它们不是线程安全的。使用专用 UI​​ 线程以外的线程访问 UI 元素可能会产生不需要的结果。

为了克服这个线程不安全的行为,应该使用 System.Windows.Forms.Timer。System.Windows.Forms.Timer 不会创建新线程或旋转新的线程池线程。它只是在 UI 线程中运行计时器回调。这样,所有 UI 交互都在单个线程中执行,从而确保线程安全。

但是,如果您的计时器回调需要大量时间才能完成,那么您的 UI 将冻结,因此 System.Windows.Forms.Timer 应该只用于非常短的任务。对于更长的后台任务,您可以使用System.ComponentModel.BackgroundWorker

于 2013-08-03T20:33:48.257 回答