0

I'm guessing I've got this a little wrong.

I am initializing two DxRichText controls and I think I have something wrong in how I have this set up. But I don't see what. I am trying to have this loading occur in parallel with other initialization.

I have the following methods:

private Task InitializeRichText(DxRichEdit editor, Organization? org,
    RichText? src, RichTextModel model)
{

    if (org == null || src == null)
        return editor.NewDocumentAsync().AsTask();

    model.RichText = src;
    model.BlobUrl = BlobService.GetBlobUrl(src.OpenXmlFilename);
    return LoadUrlAsync(editor, model.BlobUrl);
}

private async Task LoadUrlAsync(DxRichEdit editor, string url)
{
    using (var httpClient = new HttpClient())
    {
        using (var response = await httpClient.GetAsync(url))
        {
            if (response.IsSuccessStatusCode)
            {
                await using (var stream = await response.Content.ReadAsStreamAsync())
                {
                    await editor.LoadDocumentAsync(stream, DocumentFormat.OpenXml);
                }
            }
        }
    }
}

Important item: NewDocumentAsync() and LoadDocumentAsync() return a ValueTask (not a Task).

In my OnInitializedAsync() I have the following (with lots of other code in between):

var listRichTasks = new List<Task>();

await Task.Yield();
listRichTasks.Add(InitializeRichText(RichEditDesc, _organization,
                        _organization?.Description, Model.Description));
listRichTasks.Add(InitializeRichText(RichEditNews, _organization,
                        _organization?.News, Model.News));

// this call is never returning
Task.WaitAll(listRichTasks.ToArray());

What do I have wrong?

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
David Thielen
  • 28,723
  • 34
  • 119
  • 193
  • 3
    Not the source of your problem, but I recommend reading [You're using HttpClient wrong and it is destabilizing your software](https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/) and the follow-up [You're (probably still) using HttpClient wrong and it is destabilizing your software](https://josef.codes/you-are-probably-still-using-httpclient-wrong-and-it-is-destabilizing-your-software/). – ProgrammingLlama Aug 03 '23 at 03:23
  • 7
    Is there a reason why you're using `Task.WaitAll` rather than `await Task.WhenAll`? – ProgrammingLlama Aug 03 '23 at 03:27
  • @ProgrammingLlama Those HttpClient links are awesome - thank you. As to the WaitAll() vs WhenAll(). As to WaitAll() vs WhenAll() - what is the difference? – David Thielen Aug 03 '23 at 03:28
  • 3
    `WaitAll` is synchrnous code, whereas `WhenAll` returns an awaitable so you `await` it. – ProgrammingLlama Aug 03 '23 at 03:30
  • 2
    https://learn.microsoft.com/en-us/aspnet/core/blazor/components/synchronization-context?view=aspnetcore-7.0#avoid-thread-blocking-calls – JohanP Aug 03 '23 at 03:49
  • 1
    @JohanP Very helpful link - thank you – David Thielen Aug 03 '23 at 03:59
  • 1
    I would like to add that in your LoadUrlAsync method you could remove the nesting of usings by just inlining them. you can pust every using in one line without nesting parentheses: using var httpClient = new HttpClient(); using var response = await httpClient.GetAsync(url); etc. because the using scope is actually within the method only, so there is not point using parentheses in usings at all – ssedlacek Aug 03 '23 at 07:55
  • 3
    The update at the start of the question seems inappropriate to me, and I'd recommend rolling it back. A Stack Overflow question should be about *one* thing - now you've got a question which is almost entirely about `Task.WaitAll`, but with an introduction referring to a comment and inviting responses about something entirely different. If you want to ask a question about HttpClient, do that in a separate question rather than muddying the waters about what *this* question is about. – Jon Skeet Aug 03 '23 at 08:22
  • It would be best if either @DavidThielen or @ProgrammingLama wrote an answer to this focusing exclusively on the `WaitAll()` issue. – Julian Aug 03 '23 at 08:40
  • 1
    Recommended reading: [Don't block on async code](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) by Stephen Cleary. – Theodor Zoulias Aug 03 '23 at 09:19
  • @JonSkeet Good point on removing the update. I went to do so but someone beat me to it. thanks – David Thielen Aug 03 '23 at 15:01
  • @ProgrammingLlama If you put your comment as an answer, happy to select it. thanks – David Thielen Aug 03 '23 at 15:02
  • `InitializeRichText` should also be `async`, and you should do `return await return editor.NewDocumentAsync();` and `return await return LoadUrlAsync(editor, model.BlobUrl);`. See also https://blog.stephencleary.com/2016/12/eliding-async-await.html – Charlieface Aug 03 '23 at 20:53
  • @Charlieface Thank you. I was thinking the same thing but it helps to have your guidance so I know my consideration was correct. – David Thielen Aug 04 '23 at 02:50

1 Answers1

2

Per Microsoft's documentation (thanks @JohanP), you shouldn't call blocking methods in components. One of those specifically mentioned is WaitAll.

As I understand it, such calls can essentially lead to a deadlock where you're waiting (blocking) for a task to complete, and the task is waiting for the context you're currently blocking to free up so that it can return its results. Essentially they end up waiting for each other forever.

There's another method, WhenAll, which is an async method that roughly does the same thing (it also returns results), not a sync method like WaitAll.

Thus we can change your code to this:

var listRichTasks = new List<Task>();

await Task.Yield();
listRichTasks.Add(InitializeRichText(RichEditDesc, _organization,
                        _organization?.Description, Model.Description));
listRichTasks.Add(InitializeRichText(RichEditNews, _organization,
                        _organization?.News, Model.News));

// this call is never returning
await Task.WhenAll(listRichTasks.ToArray());

And this should get rid of the deadlock you seem to be experiencing.

For more on async, I recommend Stephen Cleary's blog. A pertinent article was provided in the comments: Don't Block on Async Code.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86