Comments (8)
Thread.Sleep(5000);
is blocking the caller
in async await you should use Task.Delay(5000);
this works as expected
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
BadTask().SafeFireAndForget();
Console.WriteLine("Hello World!");
Console.ReadLine();
}
static async Task BadTask()
{
await Task.Delay(5000);
Console.WriteLine("Hello World!");
}
from asyncawaitbestpractices.
I agree that this is true, however, I'm not sure how to fix it. It may just be a limitation of .NET.
Expanding on your example, even if we use async
/await
to call await BadTask()
, it will still run synchronously and Thread.Sleep
will still block the thread. (I.e. In the provided example, SafeFireAndForget
has the same behavior as await
):
static Task BadTask()
{
Thread.Sleep(5000);
return Task.CompletedTask;
}
static async Task Main(string[] args)
{
await BadTask();
Console.WriteLine("Hello World!");
}
from asyncawaitbestpractices.
If anyone has ideas on how to best resolve this Issue, please do share them!
If it turns out that we cannot resolve this issue, I'll likely convert this to a Discussion so that we can continue the conversation.
from asyncawaitbestpractices.
@brminnick adding Task.Yield() at the start will push everything below to thread pool
static async void HandleSafeFireAndForget<TException>(ValueTask valueTask, bool continueOnCapturedContext, Action<TException>? onException) where TException : Exception
{
try
{
await Task.Yield();
await valueTask.ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (_onException != null || onException != null)
{
HandleException(ex, onException);
if (_shouldAlwaysRethrowException)
throw;
}
}
from asyncawaitbestpractices.
I had the same thought, and tried adding await Task.Yeild()
last night, but because of the way .NET handles Tasks, the Task
is run synchronously until the first await
keyword is reached.
This means that HandleSafeFireAndForget
doesn't execute until the Task
is executed. In other words, HandleSafeFireAndForget
executes when the first await
is reached.
For example, in the following code, Thread.Sleep
executes synchronously, before await Task.Yield();
is reached in HandleSafeFireAndForget
:
Task.Run(() =>
{
Thread.Sleep(5000);
await Task.Delay(1000); // This is when HandleSafeFireAndForget executes
}.SafeFireAndForget();
Here's another example, using a Unit Test:
Expected: not equal to 12
But was: 12
[Test]
public async Task SafeFireAndForget_NonAsyncMethodThreadTest()
{
//Arrange
Thread initialThread, workingThread, finalThread;
var threadTCS = new TaskCompletionSource<Thread>();
//Act
initialThread = Thread.CurrentThread;
NonAsyncMethod().SafeFireAndForget();
finalThread = Thread.CurrentThread;
workingThread = await threadTCS.Task;
//Assert
Assert.IsNotNull(initialThread);
Assert.IsNotNull(workingThread);
Assert.IsNotNull(finalThread);
Assert.AreEqual(initialThread.ManagedThreadId, finalThread.ManagedThreadId);
Assert.AreNotEqual(initialThread.ManagedThreadId, workingThread.ManagedThreadId);
Assert.AreNotEqual(finalThread.ManagedThreadId, workingThread.ManagedThreadId);
async Task NonAsyncMethod()
{
threadTCS.SetResult(Thread.CurrentThread);
await Task.Delay(1000);
}
}
from asyncawaitbestpractices.
In current design it's not possible without API break, because with any call like this one:
SomeTask().SafeFireAndForget();
We will always start SomeTask first and it will execute whatever it can inside that task synchronously on the caller thread, and only after it will reach continuation (first await) and return Task
object it will be passed to SafeFireAndForget
extension method.
The only way to fix eagerness of C# Tasks is to wrap them in thunk like
SafeFireAndForget(() => SomeTask);
This will introduce breaking change in API (Func<ValueTask> valueTask
instead of ValueTask valueTask
)
static async void HandleSafeFireAndForget<TException>(Func<ValueTask> valueTask, bool continueOnCapturedContext, Action<TException>? onException) where TException : Exception
{
try
{
await Task.Yield();
await valueTask().ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (_onException != null || onException != null)
{
HandleException(ex, onException);
if (_shouldAlwaysRethrowException)
throw;
}
}
That way it will definitely execute Task.Yield or Task.Run first, jump to thread pool and then proceed safely.
It's not my call to make, but maybe just add "even more safer" API? :)
from asyncawaitbestpractices.
I thought that the purpose of this API is to provide an exception-safe and explicit way to forget about async (not background/parallel) task execution and nothing more.
Fire-and-forget is an alternative to await
and should not be safe against thread-blocking because it's a responsibility of a task itself.
Modifying existing logic with thread switching under the hood will break the compatibility for applications that fire-and-forget tasks that interact with the UI thread.
Adding SafeFireAndForgetOnBackground
may be a solution because it clearly indicates the threading aspect.
However, I dont think it's necessary because users which want to additionally switch the thread can just:
Task.Run(BadTask).SafeFireAndForget();
from asyncawaitbestpractices.
I thought that the purpose of this API is to provide an exception-safe and explicit way to forget about async (not background/parallel) task execution and nothing more.
That's why I've mentioned what's written in README.md:
SafeFireAndForget
allows a Task to safely run on a different thread while the calling thread does not wait for its completion.
It's just not true :)
from asyncawaitbestpractices.
Related Issues (20)
- What Dotnet native directives to use with .Net Native tool chain?
- Windows 11 - Smart App Control HOT 2
- Async / Await != Multithread HOT 2
- Why is there an in-keyword on a bool parameter HOT 1
- Cannot use SafeFireAndForget() with ValueTask<T>
- CanExecuteChanged doesn't fire HOT 7
- CanExecute issue HOT 2
- Error when running from signed assembly HOT 5
- Is "Sealed" necessary for AsyncCommand? HOT 2
- Execute parameter ignored HOT 3
- WinRT: Windows 8.1 SafeFireAndForget issue "No overload for method" HOT 12
- Question about capctured context in AsyncCommand HOT 1
- ArgumentException with Dynamic Handlers HOT 2
- Possible improvement for explicit ICommand.Execute() implementation in AsyncCommand? HOT 1
- Why's MVVMLight's message sent in vain? HOT 13
- Question: Cancellation HOT 1
- Add NuGet README
- Does RaiseCanExecuteChanged need to be called on the UI thread?
- Parameter change does not invoke CanExecuteChanged HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from asyncawaitbestpractices.