【发布时间】:2012-03-30 16:21:21
【问题描述】:
如何在特定旋转后在循环中使用时间延迟? 假设:
for(int i = 0 ; i<64;i++)
{
........
}
我希望每 8 次旋转后延迟 1 秒。
【问题讨论】:
如何在特定旋转后在循环中使用时间延迟? 假设:
for(int i = 0 ; i<64;i++)
{
........
}
我希望每 8 次旋转后延迟 1 秒。
【问题讨论】:
有很多方法可以做到这一点:
方法一:非常糟糕:忙碌等待:
DateTime timeToStartUpAgain = whatever;
while(DateTime.Now < timeToStartUpAgain) {}
这是一件可怕的事情;操作系统会假设你正在做有用的工作,并会分配一个 CPU 来做任何事情,除了在这上面旋转。永远不要这样做除非你知道旋转只会持续几微秒。基本上,当您这样做时,您已经雇用了某人为您看钟;这不经济。
让线程休眠也是一件可怕的事情,但没有加热 CPU 可怕。休眠一个线程告诉操作系统“这个应用程序的这个线程应该停止响应事件一段时间并且什么都不做”。这比雇人为你看钟要好;现在你已经雇了一个人为你睡觉。
这可以有效利用资源;现在你正在雇人煮鸡蛋,在煮鸡蛋的时候,他们可以做吐司。
但是,编写程序以便将工作分解为小任务是一件很痛苦的事情。
当然,C# 5 目前仅处于测试阶段。
注意:从 Visual Studio 2012 开始使用 C# 5。如果您使用的是 VS 2012 或更高版本,则可以使用异步编程。
【讨论】:
如果你想在每 8 次迭代后休眠,试试这个:
for (int i = 0; i < 64; i++)
{
//...
if (i % 8 == 7)
Thread.Sleep(1000); //ms
}
【讨论】:
使用Thread.Sleep(来自 System.Threading):
for(int i = 0 ; i<64;i++)
{
if(i % 8 == 0)
Thread.Sleep(1000);
}
【讨论】:
这是我想出的实现。它是通用的,因此您可以轻松地将其重用于您的用例。 (它与@Servy 的建议一致)
public static async Task ForEachWithDelay<T>(this ICollection<T> items, Func<T, Task> action, double interval)
{
using (var timer = new System.Timers.Timer(interval))
{
var task = new Task(() => { });
int remaining = items.Count;
var queue = new ConcurrentQueue<T>(items);
timer.Elapsed += async (sender, args) =>
{
T item;
if (queue.TryDequeue(out item))
{
try
{
await action(item);
}
finally
{
// Complete task.
remaining -= 1;
if (remaining == 0)
{
// No more items to process. Complete task.
task.Start();
}
}
}
};
timer.Start();
await task;
}
}
然后这样使用它:
var numbers = Enumerable.Range(1, 10).ToList();
var interval = 1000; // 1000 ms delay
numbers.ForEachWithDelay(i => Task.Run(() => Console.WriteLine(i + " @ " + DateTime.Now)), interval);
打印这个:
1 @ 2/2/2016 9:45:37 AM
2 @ 2/2/2016 9:45:38 AM
3 @ 2/2/2016 9:45:39 AM
4 @ 2/2/2016 9:45:40 AM
5 @ 2/2/2016 9:45:41 AM
6 @ 2/2/2016 9:45:42 AM
7 @ 2/2/2016 9:45:43 AM
8 @ 2/2/2016 9:45:44 AM
9 @ 2/2/2016 9:45:45 AM
10 @ 2/2/2016 9:45:46 AM
【讨论】:
您可能还想考虑使用Timer,而不是在循环中暂停当前线程。
它将允许您在一段时间后执行某些代码块(重复或不重复)。如果没有更多上下文,很难确定这是否最适合您的情况,或者您将如何相应地更改您的解决方案。
【讨论】:
有一次我需要编写一个程序,女巫非常敏感,需要准确的时间(大约纳秒)。
我使用了各种各样的东西,比如Thread.Sleep()、Timer 和......但它们都不是那么准确,可以满足我的需求,所以我用这个代码来延迟:
var stopWatch = new Stopwatch();
for(int i = 0 ; i<64;i++)
{
stopWatch.Restart();
while (stopWatch.ElapsedMilliseconds <= 1000)
{
}
}
当然,这种方式对 CPU 不是很友好,但是当您需要如此精确的精度时,您就必须走老路了。
【讨论】:
试试这个不依赖 async、await 或 TPL 的更简单的版本。
代码在这里
foreach (var item in collection)
{
if (cnt < 50)
{
cnt++;
DoWork(item);
}
else
{
bool stayinLoop = true;
while (stayinLoop)
{
Thread.Sleep(20000);
if (cnt < 30)
{
stayinLoop = false;
DoWork(item);
}
}
}
}
在单独的线程中工作,这样 sleep 就不会阻塞你,可以是后台 worker 、 Begin 、 End 方法的 webrequest 或可选的异步任务或 Task.Run()
function DoWork()
//Do work in a sep thread.
cnt--;
)
【讨论】: