如何正确使用异步,等待和ManualResetEvents控制的无限while循环
所以我想在这里做的是:如何正确使用异步,等待和ManualResetEvents控制的无限while循环
- 使发动机回路和工作对象上,如果队列不为空。
- 如果队列为空,我调用manualresetevent使线程休眠。
- 当一个项目被添加,并且循环未激活时,我设置manualresetevent。
- 为了让它更快,我从列表中选取最多5个项目,并对它们进行异步操作,并等待所有项目完成。
问题:
- 在两个列表中明确方法是尽快到AddToUpdateQueueMethod一个新的呼叫被称为调用。
- 因为我在等待Task.WhenAll(任务),所以线程应该在继续前等待它的完成,因此清单应该只在Task.WhenAll(任务)返回后被调用。
我在这里错过了什么,或者什么是更好的实现方法。
public async Task ThumbnailUpdaterEngine()
{
int count;
List<Task<bool>> tasks = new List<Task<bool>>();
List<Content> candidateContents = new List<Content>();
while (true)
{
for (int i = 0; i < 5; i++)
{
Content nextContent = GetNextFromInternalQueue();
if (nextContent == null)
break;
else
candidateContents.Add(nextContent);
}
foreach (var candidateContent in candidateContents)
{
foreach (var provider in interactionProviders)
{
if (provider.IsServiceSupported(candidateContent.ServiceType))
{
Task<bool> task = provider.UpdateThumbnail(candidateContent);
tasks.Add(task);
break;
}
}
}
var results = await Task.WhenAll(tasks);
tasks.Clear();
foreach (var candidateContent in candidateContents)
{
if (candidateContent.ThumbnailLink != null && !candidateContent.ThumbnailLink.Equals(candidateContent.FileIconLink, StringComparison.CurrentCultureIgnoreCase))
{
Task<bool> task = DownloadAndUpdateThumbnailCache(candidateContent);
tasks.Add(task);
}
}
await Task.WhenAll(tasks);
//Clean up for next time the loop comes in.
tasks.Clear();
candidateContents.Clear();
lock (syncObject)
{
count = internalQueue.Count;
if (count == 0)
{
isQueueControllerRunning = false;
monitorEvent.Reset();
}
}
await Task.Run(() => monitorEvent.WaitOne());
}
}
private Content GetNextFromInternalQueue()
{
lock (syncObject)
{
Content nextContent = null;
if (internalQueue.Count > 0)
{
nextContent = internalQueue[0];
internalQueue.Remove(nextContent);
}
return nextContent;
}
}
public void AddToUpdateQueue(Content content)
{
lock (syncObject)
{
internalQueue.Add(content);
if (!isQueueControllerRunning)
{
isQueueControllerRunning = true;
monitorEvent.Set();
}
}
}
您应该简单地使用TPL Dataflow。它是TPL之上的演员框架,支持async
。使用一个ActionBlock
与async
动作和MaxDegreeOfParallelism
5:
var block = new ActionBlock<Content>(
async content =>
{
var tasks = interactionProviders.
Where(provider => provider.IsServiceSupported(content.ServiceType)).
Select(provider => provider.UpdateThumbnail(content));
await Task.WhenAll(tasks);
if (content.ThumbnailLink != null && !content.ThumbnailLink.Equals(
content.FileIconLink,
StringComparison.CurrentCultureIgnoreCase))
{
await DownloadAndUpdateThumbnailCache(content);
}
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 5});
foreach (var content in GetContent())
{
block.Post(content);
}
block.Complete();
await block.Completion
因此,这将很重要while循环的内部结构,但是如何在队列为空时使while循环进入睡眠状态,并且只有在队列有某些时才会唤醒。我可以使用await Task.Sleep(someTimeout),但这不是最优的。除了块初始化之外,上面的代码将在while循环内部进行,但是如何控制while循环线程。 – 2015-02-12 17:58:56
@ Jack_2060你不需要。 TPL Dataflow为您处理所有这些事情。它有5个内部运行内容的任务,当它们完成并且队列为空时,任务完成。下次您发布到该块时,将创建一个新任务,等等。 – i3arnon 2015-02-12 19:12:46
随着TPL,你几乎永远不会需要使用'ManualResetEvent'以及诸如此类的东西,比如'Task.Run(()= > monitorEvent.WaitOne())',除非你处理一些遗留代码。看看TPL Dataflow,它为组织流水线处理提供了很多东西。 – Noseratio 2015-02-12 04:48:22
您正在寻找的是['BlockingCollection'](https://msdn.microsoft.com/en-us/library/dd267312%28v=vs.110%29.aspx) –
2015-02-12 06:46:51