计时器由任务和任务产生的计时器由计时器

时间:2015-07-22 19:49:58

标签: c# timer task

我无法用清楚的语言向自己解释为什么由Timer生成的Task工作正常,但是由Task生成的Timer不会。

下面包含所有相关代码,以便您轻松复制。

Form.cs:

private void Form1_Load(object sender, EventArgs e)
{
    ProcessDelayList list = new ProcessDelayList();

    foreach (ProcessDelay p in list)
    {
        //this works
        p.Start();

        //this does NOT work
        //Task.Factory.StartNew(() => p.Start());
    }
}

ProcessDelayList.cs:

public class ProcessDelayList : List<ProcessDelay>
{
    public ProcessDelayList()
    {
        Add(new ProcessDelay("Process 1", 2000));
        Add(new ProcessDelay("Process 2", 4000));
        Add(new ProcessDelay("Process 3", 6000));
        Add(new ProcessDelay("Process 4", 8000));
        Add(new ProcessDelay("Process 5", 10000));
    }
}

ProcessDelay.cs:

public class ProcessDelay
{
    private string name;
    private int delay;
    private Timer timer;

    public ProcessDelay(string name, int delay)
    {
        this.name = name;
        this.delay = delay;
    }
    public void Start()
    {
        timer = new Timer();
        timer.Interval = delay;
        timer.Tick += timer_Tick;
        timer.Start();
    }
    private void timer_Tick(object sender, EventArgs e)
    {
        //these work either way, as long as the task
        // is NOT spawn in the main loop.

        //TimerProc();
        TimerProcTask();
    }
    private void TimerProcTask()
    {
        Task.Factory.StartNew(() => TimerProc());
    }
    private void TimerProc()
    {
        timer.Stop();
        MessageBox.Show(name, delay.ToString());
    }
}

1 个答案:

答案 0 :(得分:8)

啊,计时器。 .NET中有四个,每个行为略有不同。您正在使用System.Windows.Forms.Timer

此计时器使用Win32消息队列来触发计时器事件(WM_TIMER)。创建计时器的线程是执行回调方法(timer_Tick)的线程。线程需要一个消息泵才能执行定时器。

告诉任务在当前的SynchronizationContext上运行将使其工作:

Task.Factory.StartNew(() => p.Start(),
                 CancellationToken.None,
                 TaskCreationOptions.LongRunning,
                 TaskScheduler.FromCurrentSynchronizationContext());

这实际上会调用在UI线程上发生的调用,所以对我来说这似乎毫无意义,如果你所做的只是调用p.Start()方法(几乎是单线程)。

请注意System.Windows.Forms.Timer类的备注部分:

  

此Windows计时器专为使用UI线程执行处理的单线程环境而设计。它要求用户代码具有可用的UI消息泵,并始终在同一个线程中运行,或者将调用编组到另一个线程上。

如果希望定时器调用实际在单独的线程上执行,则可以使用System.Threading.Timer(或此类的System.Timers.Timer包装器)。如果需要计时器回调来更新UI,则需要封送对UI线程的UI更新调用。但是,您可以确保在单独的线程上完成任何处理密集型工作,并且只在UI线程上完成最少量的代码(例如,实际更新控件)以保持响应。