✅ Periodically (asynchronously) yielding in an otherwise synchronous method
I have a long-running CPU-bound algorithm that is being executed in a Blazor WASM application. I would like to cooperatively allow the UI to update / do event loop stuff etc.
Would you think this approach would work:
Essentially we hide the
await Task.Yield()
path behind a flag, guarantueeing synchronous execution when desired, and enabling periodic yielding of the thread if not.
CCing @BenMcLean since we got to the idea together.28 Replies
Another thought: send a progress report before yielding? (but only when
periodicallyYield
is true
of course)if it's a long running algorithm it could deserve its own thread
There is only one thread (Blazor WASM app)
really? never used that
still i would use a timed CancellationTokenSource maybe instead of a raw count
or even a timer
or an await-delay loop
The for loop is just a placeholder for the expensive algorithm.
aaah ok
There is no cancellation required in the algorithm itself. We don't need delay etc. Simply asynchronously yielding every once in a while to allow the UI to do its thing
every once in a whilethat's what i mean with timer/cts/etc the while part, the how much to wait before yielding
Yeah imagine n number of complicated steps that we can put
Task.Yield()
in between.well the less complicated the better
Maybe someone else has some input/opinions on the idea?
It may work, but https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.yield?view=net-8.0 warns against doing this in the remarks.
Task.Yield Method (System.Threading.Tasks)
Creates an awaitable task that asynchronously yields back to the current context when awaited.
@SleepWellPupper does what I'm doing here make sense? I tried to implement this pattern.
https://github.com/BenMcLean/Voxel2Pixel/commit/e197e757510a50c1cb18f3a4249f0a5b14af5d87
It warns against doing this without saying what to do instead from what I can tell.
But maybe it's in that article it links to only I might be too tired to understand it at this moment.
I'll look again tomorrow or some other day.
I think the hint is that
ContinueWith
is what's preferable to keep UI responsive isntead of Task.Yield
but since the linked article says absolutely nothing about responsive UI, it isn't definite that this is what's being recommended.
Also, the linked article is from 2008, twelve years before Blazor WebAssembly was a thing. Should I really be taking its advice?
There's so much "Do this." "No, don't do this, do that!" "No, don't do that, do this other third thing!" online and it's hard to keep it all straight.
I think I just need to ignore this 2008 note from Microsoft and just do the thing that far more recent online tutorials say to do unless you've got a really compelling argument @Evyrif there's no synchronization context, or if it's configured in a certain way, it shouldn't cause any problems
I just don't know how blazor handles that stuff so it's something to consider
have you tested it?
Not yet
I am considering that instead of
maybe it should be something like
That way, all the tasks would keep yielding until told they shouldn't. That would, I think, guarantee the UI would get to run, correct?
instead of yielding i would opt for trying "dividing" this task into a series of task of shorter duration (one could almost say coroutine)
so for example after say 10~100 cycles you start getting the datetime and after ~30 msec (from the previous interruption) you return
change signature of main method to IAsyncEnumerable so that you have to
await foreach
it
then take some measurement to adjust waiting constantsThe sync context in blazor wasm is irrelevant, as there is only one thread anyway. So
ConfigureAwait(false)
won't make a difference, as scheduling the continuation to the threadpool instead of the captured context (UI context) results in the same (single) thread being used. @Evyr
Also, @tippy what's all this about enumerables now? We're not doing async enumeration, we're just interspersing async pauses in our algorithm.yes but the point was you could use enumerable to return control to the other part of the code without recurring to thread yield
Unknown User•5mo ago
Message Not Public
Sign In & Join Server To View
$close
If you have no further questions, please use /close to mark the forum thread as answered
That might be what I need actually.
Uh, I wasn't actually done asking stuff about this.
Made a new tweet about it today:
https://x.com/McLeanBen/status/1820480854357131562
Benjamin McLean (@McLeanBen) on X
I'm trying to do what you said in this blog @meziantou
https://t.co/WV6562IKL7
What confuses me is that your blog doesn't seem to guarantee that the UI will ever get to the top when there are multiple tasks, no matter how many times they yield. How does the UI get to the top?
Twitter
How's this done? I feel like I'm missing something in what you're saying.
Can we re-open this?
Maybe create a new help post and tag this one
Unknown User•5mo ago
Message Not Public
Sign In & Join Server To View
OK I think I will need to both post a newly revised version of this one AND make one about what object oriented design pattern(s) my code should follow