按任务生成计时器和按计时器生成任务
Timer Spawn By Task And Task Spawn By 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());
}
}
啊,定时器。在 .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
class 的备注部分:
This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.
如果您希望计时器调用在单独的线程上实际执行,您可以使用 System.Threading.Timer
(或此 class 的 System.Timers.Timer
包装器)。如果您需要计时器回调来更新 UI,您需要将 UI 更新调用编组到 UI 线程。但是,您可以确保任何处理密集型工作都在单独的线程上完成,并且只有最少量的代码(例如控件的实际更新)在 UI 线程上完成以保持响应。
我无法清楚地向自己解释为什么由计时器生成的任务工作得很好,但由任务生成的计时器却不能。
所有相关代码都包含在下面,因此您可以轻松地复制它。
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());
}
}
啊,定时器。在 .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
class 的备注部分:
This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.
如果您希望计时器调用在单独的线程上实际执行,您可以使用 System.Threading.Timer
(或此 class 的 System.Timers.Timer
包装器)。如果您需要计时器回调来更新 UI,您需要将 UI 更新调用编组到 UI 线程。但是,您可以确保任何处理密集型工作都在单独的线程上完成,并且只有最少量的代码(例如控件的实际更新)在 UI 线程上完成以保持响应。