异步定时器循环功能,无需停止主线程



>我用"开始"按钮创建了WPF表单,这个按钮是"StartMacros"函数。

当我单击"开始"按钮时,我想运行一个循环并每 10 秒打开一个 Firefox 浏览器,而无需停止主线程,因此我可以创建另一个"暂停"按钮或"取消"按钮

现在一切正常,但循环中没有延迟,我该如何创建它? "await Task.Delay((10000));" 不会创建延迟。

public void StartMacros(object sender, RoutedEventArgs e)
{
AsyncTimerLoop();
}
public async void AsyncTimerLoop()
{
for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
{
OpenBrowser(profileNumber);
await Task.Delay((10000));
}
}
public void OpenBrowser(int profileNumber)
{
System.Diagnostics.Process.Start("firefox.exe", "-P " + profileNumber + " -no-remote imacros://run/?m="" + ImacroFilePath + """);
}

您只需一种方法即可完成此操作:

private async void StartMacros(object sender, RoutedEventArgs e)
{
for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
{
OpenBrowser(profileNumber);
await Task.Delay(10000);
}
}

请注意,我已将async关键字添加到该方法中。

这里发生的事情是微妙的。 代码未按预期工作的原因是StartMacros同步调用AsyncTimerLoop方法。 它调用的方法,然后在主线程上非常快速地循环很多次。 在每个循环迭代中,它会调用您的OpenBrowser(仍在主线程上),然后等待对Task.Delay的调用(这实际上返回了一个可等待的Task,但您的代码对此不做任何事情 - 它只是将它们丢弃)。 由于您使用await关键字调用了它,因此它在另一个线程上执行此操作 - 每个循环迭代一个线程 - 并且只是继续在主线程上循环,在每个循环中调用OpenBrowser,直到它完成循环并退出函数;所有这一切都发生得非常迅速。 对Task.Delay的调用除了在另一个线程上延迟十秒钟然后退出外,不会执行任何操作。 它们基本上没用。

现在,您可以这样做:

private async void StartMacros(object sender, RoutedEventArgs e)
{
await AsyncTimerLoop();
}
public async Task AsyncTimerLoop()
{
for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
{
OpenBrowser(profileNumber);
await Task.Delay((10000));
}
}

请注意,我在这里添加了async关键字,就像上面的代码一样,StartMacros,我也在等待AsyncTimerLoop的调用。 为了做到这一点,我不得不将AsyncTimerLoop的返回类型更改为Task,使其可以等待。 现在,当您运行代码时,它会等待整个调用以在单独的线程上AsyncTimerLoop,立即将控制权返回到StartMacros,该立即退出,以便应用程序事件循环可以恢复,从而使应用程序响应。 同时,整个AsyncTimerLoop任务在单独的线程上运行,在每个循环迭代期间等待十秒,因此在每次调用OpenBrowser之间等待十秒。

我的代码类似于你的这个修改版本,只是更简洁。

试试这段代码,使用

var task = Task.Run(() => OpenBrowser(profileNumber));
if (task.Wait(TimeSpan.FromSeconds(10000)))
{
return task.Result;   
}   

将 System.Threading.Tasks 命名空间添加到代码中以访问任务并行库。

using System.Threading.Tasks;

public void StartMacros(object sender, RoutedEventArgs e)
{
AsyncTimerLoop();
}
public async void AsyncTimerLoop()
{
for (int profileNumber = 1; profileNumber <= 10; profileNumber++)
{
var task = Task.Run(() => OpenBrowser(profileNumber));
if (task.Wait(TimeSpan.FromSeconds(10000)))
{
return task.Result;   
} 
}
}
public void OpenBrowser(int profileNumber)
{
System.Diagnostics.Process.Start("firefox.exe", "-P " + profileNumber + " -no-remote imacros://run/?m="" + ImacroFilePath + """);
}

计时器不是更容易吗?

var timer = new Timer(o => Console.WriteLine("{0} Tick", DateTime.Now), null, 0, 6000);

最新更新