C# 计时器按任务生成和任务按计时器生成

C# 计时器按任务生成和任务按计时器生成,c#,timer,task,C#,Timer,Task,我不能很清楚地向自己解释为什么由计时器生成的任务工作得很好,而由任务生成的计时器工作得不好 所有相关代码都包含在下面,因此您可以轻松地复制它 表格C: private void Form1_Load(object sender, EventArgs e) { ProcessDelayList list = new ProcessDelayList(); foreach (ProcessDelay p in list) { //this works

我不能很清楚地向自己解释为什么由计时器生成的任务工作得很好,而由任务生成的计时器工作得不好

所有相关代码都包含在下面,因此您可以轻松地复制它

表格C:

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

啊,计时器。NET中有四种,每种行为略有不同。您正在使用

此计时器使用Win32消息队列触发计时器事件(
WM\u timer
)。创建计时器的线程是执行回调方法(
timer\u 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线程上只执行最少量的代码(例如,实际更新控件)以保持其响应性。

是否可以尝试使用新任务(()=>p.Start()).Start()@thinklarge:InvalidOperationException:Start不能对已启动的任务调用。
Task.Factory.StartNew(() => p.Start(),
                 CancellationToken.None,
                 TaskCreationOptions.LongRunning,
                 TaskScheduler.FromCurrentSynchronizationContext());