简体   繁体   English

等待直到任务完成才阻止

[英]await not blocking until Task finishes

I am trying to block RequestHandler.ParseAll() with await ConsumerTask; 我正在尝试使用await ConsumerTask;来阻止RequestHandler.ParseAll() await ConsumerTask; , but when i set a breakpoint there, i ALWAYS get the "Done..." output first... and then Parse2() fails with a NullReferenceException. ,但是当我在那里设置断点时,我总是首先获得“ Done ...”输出……然后Parse2()失败,并显示NullReferenceException。 (thats my guess: "the GC starts cleaning up because _handler got out of scope") (这就是我的猜测:“由于_handler超出范围,GC开始清理”)

Anyway, I can't figure out why that happens. 无论如何,我不知道为什么会这样。

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 has spotted the problem in comments. CodesInChaos在注释中发现了问题。 It stems from having async methods returning void , which you should almost never do - it means you've got no way to track them. 它源于异步方法返回void ,您几乎应该这样做-这意味着您无法跟踪它们。

Instead, if your async methods don't have any actual value to return, you should just make them return Task . 相反,如果您的异步方法没有要返回的实际值,则应使它们返回Task

What's happening is that ParseSpecific is only running synchronously until the first await QueryWebPage(v) that doesn't complete immediately. 发生的情况是, ParseSpecific仅同步运行,直到第一个await QueryWebPage(v)操作没有立即完成。 It's then returning... so the task started here: 然后返回...所以任务从这里开始:

await Task.Run(() => _handler.ParseSpecific(mUrls));

... completes immediately, and "Done" gets printed. ...立即完成,并打印“完成”。

Once you've made all your async methods return Task , you can await them. 一旦所有异步方法都返回Task ,就可以等待它们。 You also won't need Task.Run at all. 您也完全不需要Task.Run So you'd have: 因此,您将拥有:

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);
}

Your Reinit method also needs changing, as currently the ConsumerTask will basically complete almost immediately, as Consume will return immediately as it's another async method returning void. 您的Reinit方法也需要更改,因为当前ConsumerTask基本上会立即完成,因为Consume将立即返回,因为这是另一个返回void的异步方法。

To be honest, what you've got looks very complex, without a proper understanding of async/await. 老实说,您所拥有的内容看起来非常复杂,没有对async / await的正确理解。 I would read up more on async/await and then probably start from scratch. 我会在async / await上阅读更多内容,然后可能从头开始。 I strongly suspect you can make this much, much simpler. 我强烈怀疑您可以简化很多事情。 You might also want to read up on TPL Dataflow which is designed to make producer/consumer scenarios simpler. 您可能还想阅读TPL Dataflow ,它旨在简化生产者/消费者场景。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM