1

我在 C# 中有一个 1 秒的计时器,其中有一个 while 序列。我的问题是,如果 while 序列在 1 秒之前没有完成,计时器会滴答作响,然后从头开始重新开始 while 吗?

代码的一部分如下,它的作用是循环选择对象并改变一些东西。那么,如果选择了很多对象,我需要超过 1 秒的时间来更改它们,它们会全部更改吗?

PS我实际上希望循环被打破;大量对象只会被错误选择,但我只是想确保避免这种可能性。:)

private void timer1_Tick(object sender, EventArgs e)
        {
            TSM.ModelObjectEnumerator myEnum = null;
            myEnum = new TSM.UI.ModelObjectSelector().GetSelectedObjects();

            while (myEnum.MoveNext())
            {
                if (myEnum.Current != null)
                {....}
            }
         } 
4

7 回答 7

5

是的,计时器滴答声可以同时发生。这意味着您的计时器必须是线程安全的。

UI 计时器类 (WinForms/WPF)除外。它们的刻度函数在 UI 线程上运行。即使在DoEvents那里,您也可能导致重入,这是避免的另一个原因DoEvents

于 2013-10-08T14:40:13.770 回答
1

根据处理程序的名称,我假设您使用的是单线程的System.Windows.Forms.Timer 。这意味着该Tick事件将在前一个事件结束后触发。要中断循环,您必须在另一个线程中执行代码并使用退出条件。

我通常是这样做的:

private bool running;
private bool restart;

private void DoWork(object item)
{
    running = true;    
    TSM.ModelObjectEnumerator myEnum = null;
    myEnum = new TSM.UI.ModelObjectSelector().GetSelectedObjects();

    while (myEnum.MoveNext() && !restart)
    {       
        //do your stuff
        if (myEnum.Current != null) {....}
    }

    if(restart)
    {
        restart = false;
        ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
    }
}

private void timer1_Tick(object sender, EventArgs e)
{
    if (running)
        restart = true;
    else            
        ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));            
}
于 2013-10-08T15:21:12.050 回答
0

您总是可以尝试与此类似的方法,这样您就可以避免让多个计时器计时并启动进程。用记事本写的,所以请原谅任何严重的拼写错误

private Timer _systemTimer = null;

public MyApp()
{
_systemTimer = new Timer("how ever you set your 1 second);
// Create your event handler for when it ticks over
_systemTimer.Elapsed += new ElapsedEventHandler(systemTimerElapsed);
}

protected void systemTimerElapsed(object sender, ElapsedEventArgs e)
{
_systemTimer.Stop();
//Do what you need to do
_systemTimer.Start();
//This way if it takes longer than a second it won't matter, another time won't kick off until the previous job is done 
}
于 2013-10-08T14:46:38.010 回答
0

我会让你很容易;在另一个后台线程中使用 Thread.Sleep() 就完成了!

如果您知道什么时候完成,那么您可以使用 AutoResetEvent 来保持线程同步。如果你对更新无回调没有任何控制,时间不详建议增加你的定时器间隔!

var thread = new Thread((ThreadStart)delegate
        {
         While(true)
           {
             TSM.ModelObjectEnumerator myEnum = null;
             myEnum = new TSM.UI.ModelObjectSelector().GetSelectedObjects();
             while (myEnum.MoveNext())
              {
                  if (myEnum.Current != null)
                 {....}
              }

              Thread.Sleep(1000);
           }
         }
    thread.Start();
于 2013-10-08T14:47:05.923 回答
0

一种解决方法是在 while 事件的顶部禁用计时器,并在退出 while 事件时重新启用它。

于 2013-10-08T14:40:45.117 回答
0

while 循环不会被中断,因为计时器再次计时。但无论如何,最好的办法是在事件处理程序开始时禁用计时器,并在结束时重新启用它。

于 2013-10-08T14:41:54.107 回答
0

从 txtString 的字符串中获取每个字符,并使用 timerControl 一个一个字符地写在标签上

int g = 0;
private void timerString_Tick(object sender, EventArgs e)
{
    string a = txtString.Text;
    int em = txtString.TextLength;
    if (g < em)
    {
        lblString.Text = lblString.Text + a[g];
        g++;
    }
    else timerString.Stop();
}

来自

private void btnStringStart_Click(object sender, EventArgs e)
{
    timerString.Start();
    lblString.Text = "";
}
于 2017-03-22T08:46:12.350 回答