You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Opening this issue to discuss the "yield to thread pool or no-op if already on it" semantics, specifically:
What's the most efficient way to do it (taking runtime async into account too, cc. @jakobbotsch)
Whether it'd make sense to have a dedicated API for this in the BCL (if so, happy to open a separate proposal)
We do this extensively in the Microsoft Store, and especially for GUI apps it's quite useful. Very often people end up making the UI sluggish because they accidentally start UI-driven synchronous CPU-bound work on the UI thread. Using an API like this makes it easy to just offload to a thread pool thread instead (eventually switching back to the UI thread if needed through some other dispatcher API, out of scope here).
Here's the simplest possible implementation for this that I have:
publicstaticclassTaskSchedulerExtensions{extension(TaskScheduler){[MethodImpl(MethodImplOptions.AggressiveInlining)]publicstaticTaskSchedulerYieldAwaitableYield(){returndefault;}}[EditorBrowsable(EditorBrowsableState.Never)]publicreadonlystructTaskSchedulerYieldAwaitable{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicAwaiterGetAwaiter(){returndefault;}[EditorBrowsable(EditorBrowsableState.Never)]publicreadonlystructAwaiter:ICriticalNotifyCompletion{privatestaticreadonlyWaitCallbackOnCompletedCallback=static state =>Unsafe.As<Action>(state!)();publicboolIsCompleted{[MethodImpl(MethodImplOptions.AggressiveInlining)]get=>Thread.CurrentThread.IsThreadPoolThread;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicvoidGetResult(){}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicvoidOnCompleted(Actioncontinuation){ThreadPool.QueueUserWorkItem(OnCompletedCallback,continuation);}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicvoidUnsafeOnCompleted(Actioncontinuation){ThreadPool.UnsafeQueueUserWorkItem(OnCompletedCallback,continuation);}}}}
So you can just do:
awaitTaskScheduler.Yield();
And you're on the thread pool, or if you already were, then this call is a no-op.
Questions:
Is this the best and most efficient way to do this, or could this be optimized
Will this be de-optimized with runtime async or can it still be efficient?
Should we have a built-in API in the BCL for this?
Opening this issue to discuss the "yield to thread pool or no-op if already on it" semantics, specifically:
We do this extensively in the Microsoft Store, and especially for GUI apps it's quite useful. Very often people end up making the UI sluggish because they accidentally start UI-driven synchronous CPU-bound work on the UI thread. Using an API like this makes it easy to just offload to a thread pool thread instead (eventually switching back to the UI thread if needed through some other dispatcher API, out of scope here).
Here's the simplest possible implementation for this that I have:
So you can just do:
And you're on the thread pool, or if you already were, then this call is a no-op.
Questions:
cc. @VSadov
Note
Not using
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);because that always yields.