每隔X秒执行特定的函数

83

我有一个用C#编写的Windows窗体应用程序。以下函数用于检查打印机是否联机:

public void isonline()
{
    PrinterSettings settings = new PrinterSettings();
    if (CheckPrinter(settings.PrinterName) == "offline")
    {
        pictureBox1.Image = pictureBox1.ErrorImage;
    }
}
并在打印机离线时更新图像。现在,我该如何每2秒执行一次此函数isonline(),以便当我拔掉打印机时,在不重新启动应用程序或进行手动检查的情况下,表单上显示的图像(pictureBox1)会变成另一个图像?(例如,通过按下运行isonline()函数的“刷新”按钮)

6
轮询不是一个好主意,最好监听状态变更通知(如果有的话)。 - David Heffernan
7个回答

137

使用System.Windows.Forms.Timer

private Timer timer1; 
public void InitTimer()
{
    timer1 = new Timer();
    timer1.Tick += new EventHandler(timer1_Tick);
    timer1.Interval = 2000; // in miliseconds
    timer1.Start();
}

private void timer1_Tick(object sender, EventArgs e)
{
    isonline();
}

你可以在Form1_Load()中调用InitTimer()函数。


这个会在应用启动后自动执行吗?对我来说它不起作用。你能提供一些解决方案吗?谢谢。 - user2886091
只要计时器没有被处理或停止,它就应该工作。 - Stecya
new EventHandler 还是必要的吗? - anon
事件处理程序部分出现了错误,如 http://prntscr.com/e1sl9t 截图所示。 - user6053405
7
现今Timer对象的配置似乎有所不同。我使用以下方式使其工作:timer = new Timer(TimerCallback, null, 0, timeStepInMilliseconds) + private static void TimerCallback(Object stateInfo) - Xavier Peña
这个可以在不影响UI线程的情况下工作,不像System.Threading.Thread.Sleep()。有人能比较一下它的性能和资源利用率吗? - Neo

24

.NET 6新增了PeriodicTimer类。

var periodicTimer= new PeriodicTimer(TimeSpan.FromSeconds(1));
while (await periodicTimer.WaitForNextTickAsync())
{
    // Place function in here..
    Console.WriteLine("Printing");
}

你可以使用以下方法在后台运行它:

async Task RunInBackground(TimeSpan timeSpan, Action action)
{
    var periodicTimer = new PeriodicTimer(timeSpan);
    while (await periodicTimer.WaitForNextTickAsync())
    {
        action();
    }
}

RunInBackground(TimeSpan.FromSeconds(1), () => Console.WriteLine("Printing"));
< p > PeriodicTimer 相对于 Timer.Delay 循环的主要优势在于执行耗时任务时最为明显。

using System.Diagnostics;

var stopwatch = Stopwatch.StartNew();
// Uncomment to run this section
//while (true)
//{
//    await Task.Delay(1000);
//    Console.WriteLine($"Delay Time: {stopwatch.ElapsedMilliseconds}");
//    await SomeLongTask();
//}

//Delay Time: 1007
//Delay Time: 2535
//Delay Time: 4062
//Delay Time: 5584
//Delay Time: 7104

var periodicTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(1000));
while (await periodicTimer.WaitForNextTickAsync())
{
    Console.WriteLine($"Periodic Time: {stopwatch.ElapsedMilliseconds}");
    await SomeLongTask();
}

//Periodic Time: 1016
//Periodic Time: 2027
//Periodic Time: 3002
//Periodic Time: 4009
//Periodic Time: 5018

async Task SomeLongTask()
{
    await Task.Delay(500);
}

PeriodicTimer 会尝试每隔 n * delay 秒执行一次,而 Timer.Delay 会在每隔 n * (delay + 方法运行时间) 秒后执行一次,导致执行时间逐渐失去同步。


1
这与在无限循环中使用Task.Delay有何不同? - Adassko
1
显然,周期计时器比Task.Delay更高效,因为它没有重复任务或计时器分配 来源。这可能更容易阅读/理解,并且可能标准化计时器循环。 - DaemonFire

11

最适合初学者的解决方案是:

从工具箱中拖动一个计时器控件,为其命名,设置所需时间间隔,并将“启用”设为“True”。然后双击该计时器控件,Visual Studio(或您使用的其他工具)将为您编写以下代码:

private void wait_Tick(object sender, EventArgs e)
{
    refreshText(); // Add the method you want to call here.
}

不必担心将其粘贴到错误的代码块中或类似情况。


1
太棒了,正是我想要的。 - blind Skwirl
1
简洁明了的解决方案 - Syed Irfan Ahmad

6

随着时间的推移,事情已经发生了很大的变化。 您可以使用以下解决方案:

static void Main(string[] args)
{
    var timer = new Timer(Callback, null, 0, 2000);

    //Dispose the timer
    timer.Dispose();
}
static void Callback(object? state)
{
    //Your code here.
}

4
这个 Timer 来自 System.Threading 命名空间。 - Krzysztof Juszcze

6

线程化:

    /// <summary>
    /// Usage: var timer = SetIntervalThread(DoThis, 1000);
    /// UI Usage: BeginInvoke((Action)(() =>{ SetIntervalThread(DoThis, 1000); }));
    /// </summary>
    /// <returns>Returns a timer object which can be disposed.</returns>
    public static System.Threading.Timer SetIntervalThread(Action Act, int Interval)
    {
        TimerStateManager state = new TimerStateManager();
        System.Threading.Timer tmr = new System.Threading.Timer(new TimerCallback(_ => Act()), state, Interval, Interval);
        state.TimerObject = tmr;
        return tmr;
    }

正则表达式

    /// <summary>
    /// Usage: var timer = SetInterval(DoThis, 1000);
    /// UI Usage: BeginInvoke((Action)(() =>{ SetInterval(DoThis, 1000); }));
    /// </summary>
    /// <returns>Returns a timer object which can be stopped and disposed.</returns>
    public static System.Timers.Timer SetInterval(Action Act, int Interval)
    {
        System.Timers.Timer tmr = new System.Timers.Timer();
        tmr.Elapsed += (sender, args) => Act();
        tmr.AutoReset = true;
        tmr.Interval = Interval;
        tmr.Start();

        return tmr;
    }

2
你可以通过在表单中添加一个定时器(从设计器中),并将其Tick函数设置为运行你的isonline函数来轻松完成此操作。

2
using System;
using System.Timers;
namespace SnirElgabsi
{
  class Program
  {
     private static Timer timer1;
     static void Main(string[] args)
     {
         timer1 = new Timer(); //new Timer(1000);
         timer1.Elpased += (sender,e) =>
         {
            MyFoo();
         }
         timer1.Interval = 1000;//miliseconds
         timer1.Start();
       
         Console.WriteLine("press any key to stop");
         Console.ReadKey();
     }

     private static void MyFoo()
     {
         Console.WriteLine(string.Format("{0}", DateTime.Now));
     }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接