[英]await not blocking until Task finishes
我正在尝试使用await ConsumerTask;
来阻止RequestHandler.ParseAll()
await ConsumerTask;
,但是当我在那里设置断点时,我总是首先获得“ Done ...”输出……然后Parse2()
失败,并显示NullReferenceException。 (这就是我的猜测:“由于_handler
超出范围,GC开始清理”)
无论如何,我不知道为什么会这样。
class MainClass
{
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
/* fill mUrls here with values */
await Task.Run(() => _handler.ParseSpecific(mUrls));
Console.WriteLine("Done...");
}
}
static class Parser
{
public static async Task<IEnumerable<string>> QueryWebPage(string url) { /*Query the url*/ }
public static async Task Parse1(Query query)
{
Parallel.ForEach(/*Process data here*/);
}
public static async Task Parse2(Query query)
{
foreach(string line in query.WebPage)
/* Here i get a NullReference exception because query.WebPage == null */
}
}
sealed class RequestHandler
{
private BlockingCollection<Query> Queue;
private Task ConsumerTask = Task.Run(() => /* call consume() for each elem in the queue*/);
private async void Consume(Query obj)
{
await (obj.BoolField ? Parser.Parse1(obj) : Parser.Parse2(obj));
}
public async void ParseSpecific(string[] urls)
{
foreach(string v in urls)
Queue.Add(new Query(await QueryWebPage(v), BoolField: false));
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
private async Task ParseAll(bool onlySome)
{
ReInit();
Parallel.ForEach(mCollection, v => Queue.Add(new Query(url, BoolField:false)));
Queue.CompleteAdding();
await ConsumerTask;
/* Process stuff further */
}
}
struct Query
{
public readonly string[] WebPage;
public readonly bool BoolField;
public Query(uint e, IEnumerable<string> page, bool b) : this()
{
Webpage = page.ToArray();
BoolField = b;
}
}
CodesInChaos在注释中发现了问题。 它源于异步方法返回void
,您几乎不应该这样做-这意味着您无法跟踪它们。
相反,如果您的异步方法没有要返回的实际值,则应使它们返回Task
。
发生的情况是, ParseSpecific
仅同步运行,直到第一个await QueryWebPage(v)
操作没有立即完成。 然后返回...所以任务从这里开始:
await Task.Run(() => _handler.ParseSpecific(mUrls));
...立即完成,并打印“完成”。
一旦所有异步方法都返回Task
,就可以等待它们。 您也完全不需要Task.Run
。 因此,您将拥有:
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
await _handler.ParseSpecific(mUrls);
Console.WriteLine("Done...");
}
...
public async TaskParseSpecific(string[] urls)
{
foreach(string v in urls)
{
// Refactored for readability, although I'm not sure it really
// makes sense now that it's clearer! Are you sure this is what
// you want?
var page = await QueryWebPage(v);
Queue.Add(new Query(page, false);
}
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
您的Reinit
方法也需要更改,因为当前ConsumerTask
基本上会立即完成,因为Consume
将立即返回,因为这是另一个返回void的异步方法。
老实说,您所拥有的内容看起来非常复杂,没有对async / await的正确理解。 我会在async / await上阅读更多内容,然后可能从头开始。 我强烈怀疑您可以简化很多事情。 您可能还想阅读TPL Dataflow ,它旨在简化生产者/消费者场景。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.