具有动态延迟的C#任务
我有一个函数,需要一次处理项目3,如果所花费的总时间小于x秒,线程应该在继续之前休眠剩余的秒 因此,我正在做以下工作:具有动态延迟的C#任务,c#,parallel-processing,task,async-await,C#,Parallel Processing,Task,Async Await,我有一个函数,需要一次处理项目3,如果所花费的总时间小于x秒,线程应该在继续之前休眠剩余的秒 因此,我正在做以下工作: private void ProcessItems() { for (int i = 0, n = items.Count; i < n; i++) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start();
private void ProcessItems()
{
for (int i = 0, n = items.Count; i < n; i++)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
batch.Add(items[i]);
if (batch.Count == 3 || i >= items.Count - 3)
{
List<Task> tasks = new List<Task>(3);
foreach (Item item in batch)
tasks.Add(Task.Factory.StartNew(() => ProcessItem(item)));
Task.WaitAll(tasks.ToArray());
batch.Clear();
}
stopwatch.Stop();
int elapsed = (int)stopwatch.ElapsedMilliseconds;
int delay = (3000) - elapsed;
if (delay > 0)
Thread.Sleep(delay);
}
}
private void ProcessItems()
{
对于(int i=0,n=items.Count;i=items.Count-3)
{
列表任务=新列表(3);
foreach(批量项目)
tasks.Add(Task.Factory.StartNew(()=>ProcessItem(item));
Task.WaitAll(tasks.ToArray());
batch.Clear();
}
秒表;
运行时间=(int)stopwatch.ElapsedMilliseconds;
整数延迟=(3000)-已过;
如果(延迟>0)
睡眠(延迟);
}
}
ProcessItem函数发出webrequest并处理响应(回调)。这是一个花费少量时间的函数
但是,如果我正确理解任务,一个线程可以有多个任务。因此,如果我休眠线程,其他任务可能会受到影响
是否有更有效的方法来实现上述目标,并且可以在并行中使用任务。Foreach?使用
Task.Delay
反而
static async Task DoSomeProcess()
{
await Task.Delay(3000);
}
你说得对,线程。睡眠会阻止其他任务
是的,您可以将异步/等待模式与并行模式配对。任务在自动管理的线程上运行。阻塞线程本质上没有什么错误。只是有点浪费 下面是我将如何非常干净地实现这一点:
MyItem[] items = ...;
foreach(MyItem[] itemsChunk in items.AsChunked(3)) {
Parallel.ForEach(itemsChunk, item => Process(item));
//here you can insert a delay
}
这不会浪费一个线程,而且非常简单
Parallel.ForEach
也使用当前线程处理工作项,因此它不会处于空闲状态。您还可以添加延迟逻辑。实现是留给读者的一个练习。。。此函数用于将列表拆分为给定大小的块(3)。这样一个助手函数的好处是它将批处理逻辑从重要部分中分离出来。您的ProcessItems
方法可以很容易地转换为异步版本ProcessItemsAsync
(我没有验证“批处理”逻辑):
这本身就是一个阻塞调用,可能会扼杀我们刚刚获得的
async
的优势。你能否在你的应用程序中完全消除这样的块,很大程度上取决于应用程序的其余工作流程。是的。如果您不想为此烧掉线程,请使用wait Task.Delay
。不过,这会迫使您使用整个异步模型。您必须将所有呼叫者也更改为使用任务。如果线程很少,那么从性能的角度来看,选择哪个模型并不重要,因为线程的开销是恒定的。与异步相比,它不会使在其上运行的东西变慢或变快。如果您不需要异步,我建议您不要使用它,因为它会使事情复杂化。Wait Task.Delay如何适合您的示例?您可以在标记的位置插入Wait Task.Delay(3000)
,标记方法async
,将返回类型更改为Task
,现在必须更改所有调用者以等待返回的任务。我认为您应该研究一下异步是如何工作的,然后决定是否需要它。可能吧:不是。您是否关心某个特定问题?因此等待任务。延迟(3000)返回一个等待3000的空任务,然后?@Ivan MarkDebono否,Task.Delay()
返回一个等待给定时间的Task
,wait Task.Delay()
异步等待该时间。是否有完整源代码工作的最终解决方案?
private async Task ProcessItemsAsync()
{
for (int i = 0, n = items.Count; i < n; i++)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
batch.Add(items[i]);
if (batch.Count == 3 || i >= items.Count - 3)
{
List<Task> tasks = new List<Task>(3);
foreach (Item item in batch)
tasks.Add(Task.Run(() => ProcessItem(item)));
await Task.WhenAll(tasks.ToArray());
batch.Clear();
}
stopwatch.Stop();
int elapsed = (int)stopwatch.ElapsedMilliseconds;
int delay = (3000) - elapsed;
if (delay > 0)
await Task.Delay(delay);
}
}
ProcessItemsAsync().Wait();