Using Delegates in C# Asynchronously - c#

I have the following delegate
delegate void UpdateFileDelegate(long maxFileID);
That I am calling from a WinForms app like so
UpdateFileDelegate FD = new UpdateFileDelegate(ClassInstance.UpdateFile);
FD.BeginInvoke(longIDNumber,null,null);
It runs asynchronously but the question I have is how can I tell when the Method is done executing so I can let the end user know?
Update:
Thanks to the recommendations below the following code does the trick.
Also this article was helpful in getting me to understand what my code is actually doing.
delegate void UpdateFileDelegate(long maxFileID);
UpdateFileDelegate FB = new UpdateFileDelegate(ClassInstance.UpdateFile);
AsyncCallback callback = new AsyncCallback(this.CallBackMethod);
IAsyncResult result = FB.BeginInvoke(longIDNumber);
private void CallBackMethod(IAsyncResult result)
{
AsyncResult delegateResult = (AsyncResult)result;
UpdateFileDelegate fd = (UpdateFileDelegate)delegateResult.AsyncDelegate;
fd.EndInvoke(result);
MessageBox.Show("All Done!");
}

See Calling Synchronous Methods Asynchronously
The BeginInvoke will return an IAsyncResult, which enables a number of different ways to be aware of when it is done, such as using its AsyncWaitHandle.WaitOne() method. It depends on what you are doing in the meantime.
Or you can pass a delegate for a callback method to BeginInvoke. This is arguably the most powerful strategy, but sometimes is overkill.

Calling EndInvoke on the returned IAsyncResult reference is very important. It is the only way to find out if the delegate target finished executing without any exceptions. If you don't, such an exception will fall into the bit-bucket and your program will silently fail to execute properly. You can call EndInvoke either on the same thread that called BeginInvoke() or you can do it in a callback. Calling it on the same thread rarely is useful, you'd almost always lose the benefits of asynchronous execution. Some sample code that demonstrates both and emphasizes the exception handling:
using System;
using System.Runtime.Remoting.Messaging;
class Program {
static void Main(string[] args) {
new Program().Run();
Console.ReadLine();
}
void Run() {
Action example = new Action(threaded);
IAsyncResult ia = example.BeginInvoke(new AsyncCallback(completed), null);
// Option #1:
/*
ia.AsyncWaitHandle.WaitOne();
try {
example.EndInvoke(ia);
}
catch (ApplicationException ex) {
Console.WriteLine(ex.Message);
}
*/
}
void threaded() {
throw new ApplicationException("Kaboom");
}
void completed(IAsyncResult ar) {
// Option #2:
Action example = (ar as AsyncResult).AsyncDelegate as Action;
try {
example.EndInvoke(ar);
}
catch (ApplicationException ex) {
Console.WriteLine(ex.Message);
}
}
}
You should not use a try block in the completion callback if you don't expect the code to throw exceptions. This ensures your program terminates when it does.

Related

Stuck with async await thread

In constructor I want to call one method type :
private async Task OnLoadPrometDanKorisnikDatum
and I want to wait that method while its finish, and I have more method(3) like this and I want to call this 3 methods in background thread and don't wait him to finish, just want to wait first method. And I want to them executing parallel.
I have methods async Task,and in constructor of view model I call like this
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat,
DatumVrednost).Wait();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).Wait();
if I don't place .Wait() on the end, program doesn't work. I see in debug mode they run asynchronly, but time spent tell me that they sub(one method time + second method time + ....).
Can someone help me, this is for me very stuf...
Answer
The best way to handle your scenario is to use async void.
I recommend first reading the Explanation section below to fully understand the best practices around async void.
public MyConstructor()
{
ExecuteAsyncMethods();
}
async void ExecuteAsyncMethods()
{
try
{
await OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost);
await OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja);
}
catch(Exception e)
{
//Handle Exception
}
}
Explanation
Many C# devs are taught "Never use async void", but this is one of the few use-cases for it.
Yes async void can be dangerous and here's why:
Cannot await an async avoid method
Can lead to race conditions
Difficult to catch an Exception thrown by async void methods
E.g. the following try/catch block will not catch the Exception thrown here:
public MyConstructor()
{
try
{
//Cannot await `async void`
AsyncVoidMethodWithException();
}
catch(Exception e)
{
//Will never catch the `Exception` thrown in `AsyncVoidMethodWithException` because `AsyncVoidMethodWithException` cannot be awaited
}
//code here will be executing by the time `AsyncVoidMethodWithException` throws the exception
}
async void AsyncVoidMethodWithException()
{
await Task.Delay(2000);
throw new Exception();
}
That being said, as long as we wrap the contents of our entire async void in a try/catch block, we will be able to catch the exception, like so:
public MyConstructor()
{
AsyncVoidMethodWithException();
}
async void AsyncVoidMethodWithException()
{
try
{
await Task.Delay(2000);
throw new Exception();
}
catch(Exception e)
{
//Exception will be caught and successfully handled
}
}
SafeFireAndForget
I created a library to help with this and its additional benefit is that it avoids writing async void code that could be potentially misused by future devs.
It's open source and also available on NuGet:
Source Code
NuGet Package
SafeFireAndForget
SafeFireAndForget allows us to safely execute a Task whilst not blocking the calling thread and without waiting for it to finish before moving to the next line of code.
Below is a simplified version of SafeFireAndForget that you can add to your project.
However, I recommend copy/pasting its complete source code or adding its NuGet Package to your library to get a more robust implementation
public static async void SafeFireAndForget<TException>(this Task task, Action<TException> onException = null, bool continueOnCapturedContext = false) where TException : Exception
{
try
{
await task.ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (onException != null)
{
onException(ex);
}
}
Using SafeFireAndForget
To use SafeFireAndForget, append it to your method call like so:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget();
To handle any Exception thrown by that Task, use onException. Here's an example that prints the Exception to the Debug Console:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget(ex => Debug.WriteLine(ex));
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget(ex => Debug.WriteLine(ex));

Object synchronization method was called from an unsynchronized block of code. Exception on Mutex.Release()

I have found different articles about this exception but none of them was my case.
Here is the source code:
class Program
{
private static Mutex mutex;
private static bool mutexIsLocked = false;
static void Main(string[] args)
{
ICrmService crmService =
new ArmenianSoftware.Crm.Common.CrmServiceWrapper(GetCrmService("Armsoft", "crmserver"));
//Lock mutex for concurrent access to workflow
mutex = new Mutex(true, "ArmenianSoftware.Crm.Common.FilterCtiCallLogActivity");
mutexIsLocked = true;
//Create object for updating filtered cti call log
ArmenianSoftware.Crm.Common.FilterCtiCallLog filterCtiCallLog =
new ArmenianSoftware.Crm.Common.FilterCtiCallLog(crmService);
//Bind events
filterCtiCallLog.CtiCallsRetrieved += new EventHandler<ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs>(filterCtiCallLog_CtiCallsRetrieved);
//Execute filter
try
{
filterCtiCallLog.CreateFilteredCtiCallLogSync();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (mutexIsLocked)
{
mutexIsLocked = false;
mutex.ReleaseMutex();
}
}
}
static void filterCtiCallLog_CtiCallsRetrieved(object sender,
ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs e)
{
tryasasas
{
if (mutexIsLocked)
{
mutexIsLocked = false;
mutex.ReleaseMutex();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
filterCtiCallLog.CreateFilteredCtiCallLogSync(); function executes requests to server, and raises some events, one of which is CtiCallsRetrieve event. And I need to release the mutex when this event is fired. But on calling the mutex.Release() function exception is thrown. CreateFilteredCtiCallLogSync works synchronously. What is the problem?
Keeping a bool around that indicates that the mutex is owned is a grave mistake. You are not making the bool thread-safe. You got into this pickle because you are using the wrong synchronization object. A mutex has thread-affinity, the owner of a mutex is a thread. The thread that acquired it must also be the one that calls ReleaseMutex(). Which is why your code bombs.
You in all likelihood need an event here, use AutoResetEvent. Create it in the main thread, call Set() in the worker, WaitOne() in the main thread to wait for the worker to complete its job. And dispose it afterwards. Also note that using a thread to perform a job and having your main thread wait for its completion is not productive. You might as well have the main thread do the job.
If you are actually doing this to protect access to an object that's not thread-safe (it isn't clear) then use the lock statement.
Another reason why this exception may occur:
if (Monitor.TryEnter(_lock))
{
try
{
... await MyMethodAsync(); ...
}
finally
{
Monitor.Exit(_lock);
}
}
I get this exception on Monitor.Exit when after 'await' another thread continues execution.
Edit:
Use SemaphoreSlim, because it doesn't require releasing thread to be the same.
You will also run into this exception if you do the following:
mutex.WaitOne();
… Some Work...
await someTask;
mutex.ReleaseMutex();
That's because the code after the await can be executed on a different thread from the line just before. Basically, it seems that if you asynch code now (in early 2020), Mutexes simply don't work. Use events or something.
I have found the problem. First several things about the filterCtiCallLog class. I have designed it so to work both asynchronous and synchronous. For first I have written code for asynchronous execution. I needed a way to trigger events from child worker thread to parent, to report the working state. For this I have used AsyncOperation class and it's post method. Here is the code part for triggering CtiCallsRetrieved event.
public class FilterCtiCallLog
{
private int RequestCount = 0;
private AsyncOperation createCallsAsync = null;
private SendOrPostCallback ctiCallsRetrievedPost;
public void CreateFilteredCtiCallLogSync()
{
createCallsAsync = AsyncOperationManager.CreateOperation(null);
ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost);
CreateFilteredCtiCallLog();
}
private void CreateFilteredCtiCallLog()
{
int count=0;
//do the job
//............
//...........
//Raise the event
createCallsAsync.Post(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count));
//...........
//...........
}
public event EventHandler<CtiCallsRetrievedEventArgs> CtiCallsRetrieved;
private void CtiCallsRetrievedPost(object state)
{
CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs;
if (CtiCallsRetrieved != null)
CtiCallsRetrieved(this, args);
}
}
As you can see the code is executing synchronously. The problem here is in AsyncOperation.Post() method. I presumed that if it is called in the main thread it will act as simply triggering the event, not posting it to parent thread. However it wasn't the case. I don't know how it is working, but I have changed the code, to check if the CreateFilteredCtiCallLog is called sync or async. And if it is async call I used AsyncOperation.Post method, if not, I have simply triggered the EventHandler if it is not null. Here is the corrected code
public class FilterCtiCallLog
{
private int RequestCount = 0;
private AsyncOperation createCallsAsync = null;
private SendOrPostCallback ctiCallsRetrievedPost;
public void CreateFilteredCtiCallLogSync()
{
createCallsAsync = AsyncOperationManager.CreateOperation(null);
ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost);
CreateFilteredCtiCallLog(false);
}
private void CreateFilteredCtiCallLog(bool isAsync)
{
int count=0;
//do the job
//............
//...........
//Raise the event
RaiseEvent(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count),isAsync);
//...........
//...........
}
public event EventHandler<CtiCallsRetrievedEventArgs> CtiCallsRetrieved;
private void RaiseEvent(SendOrPostCallback callback, object state, bool isAsync)
{
if (isAsync)
createCallsAsync.Post(callback, state);
else
callback(state);
}
private void CtiCallsRetrievedPost(object state)
{
CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs;
if (CtiCallsRetrieved != null)
CtiCallsRetrieved(this, args);
}
}
Thanks everybody for the answers!
I have seen this happen when you lock code using a Monitor, then call an async code and you get this, when using a lock(object) you get a compiler error, however between monitor.enter(object) and Monitor.Exist(object) the compiler does not complain... unfortunately.
Using a flag to attempt to monitor a kernel synchro object state will just not work - the point of using those synchro calls is that they work correctly without any explicit checking. Setting flags will just cause intermittent problems because the flag may be changed inappropriately due to interrupts between checking the flag and acting on it.
A mutex can only be released by the threat that acquired it. If you callback is called by a different thread, (one internal to CreateFilteredCtiCallLogSync() or a kernel thread pool), the release will fail.
It's not clear exactly what you are attempting to do. Presumably, you want to serialize access to CreateFilteredCtiCallLogSync() and the callback flags that the instance is available for re-use? If so, you could use a semaphore instead - init. it to one unit, wait for it at the start and release it in the callback.
Is there some issue where sometimes the callback is not called, and hence the try/finally/release? If so this way out seems a bit dodgy if the callback is asychronous and may be called by another thread after the setup thread has left the function.
I only had this one once or twice, and in every case it came about by trying to release a mutex I didn't own.
Are you sure the events are raised on the same thread the mutex was acquired on?
Although you mention that filterCtiCallLog.CreateFilteredCtiCallLogSync() is a blocking call, perhaps it spawns of worker threads that raise the event?
Maybe not the most meaningful error message, I've seen this happen in some third party code as below,
object obj = new object();
lock (obj)
{
//do something
Monitor.Exit(obj);//obj released
}//exception happens here, when trying to release obj
I have read the thread and got some ideas. But did not know what exactly need to do to solve the issue. I face the same error when uploading the image to the s3 at nopCommerce solution.And the below code is working for me.
using var mutex = new Mutex(false, thumbFileName);
mutex.WaitOne();
try
{
if (pictureBinary != null)
{
try
{
using var image = SKBitmap.Decode(pictureBinary);
var format = GetImageFormatByMimeType(picture.MimeType);
pictureBinary = ImageResize(image, format, targetSize);
}
catch
{
}
}
if (s3Enabled)
//await S3UploadImageOnThumbsAsync(thumbFileName, pictureBinary, picture.MimeType, picture, targetSize);
// The above code was causing the issue. Because it is wait for the thread.
//So I replace the code below line and the error disappear. This also kind of same implementation by nopCommerce.
//The thread need to wait.
S3UploadImageOnThumbsAsync(thumbFileName, pictureBinary, picture.MimeType, picture, targetSize).Wait();
else
File.WriteAllBytes(thumbFilePath, pictureBinary);
}
finally
{
mutex.ReleaseMutex();
}

How to catch errors from worker threads in console application written in C#

I currently have a small console application that runs a number of Tasks (using Parallel.ForEach) and each one of these tasks creates sub-threads using ThreadPool.QueueUserWorkItem.
I would like the application to handle any exception thrown by these tasks/threads.
Will surrounding the Parallel.ForEach statement with try..catch work if the threads throw any errors or will they just die out?
EDIT: These sub-threads simulate users of the system. Refer to this question.
Surrounding the statement will not do the job. You can do something like this:
public static void Main(string[] args)
{
string[] files = System.IO.Directory.GetFiles(#".", "*.*");
Parallel.ForEach(files, x =>
{
try
{
MyAction(x);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
});
}
static void MyAction(string x)
{
throw new ApplicationException("Testing: " + x);
}
Don't use QUWI. I have a brief comparison of background task types on my blog (Task, BackgroundWorker, Delegate.BeginInvoke, ThreadPool.QueueUserWorkItem, and Thread).
For background tasks, Task is the clear winner. QueueUserWorkItem is very low-level by comparison.
In particular, your problem is error propogation, and Task has built-in support for this that is entirely lacking in QueueUserWorkItem. You could build it in yourself by wrapping your delegate in a try/catch, storing the exception as part of the delegate argument (or as a bound variable of a lambda expression), explicitly checking it later, and doing some technically unsupported reflection to preserve the stack trace.
But why bother? Task supports error propogation out of the box.
you can handle all exceptions with try/catch, for example:
try
{
MyParallelMethod();
}
catch(Exception e)
{
//...
}
and in your method, do something like that:
public void MyParallelMethod()
{
var data = new List<String>();
//...
Parallel.ForEach(data, d =>
{
try
{
//...
}
catch (Exception e)
{
//...
}
});
}
Will surrounding the Parallel.ForEach statement with try..catch work if the threads throw any errors or will they just die out?
No, you need to put try/catch inside the sub threads.
Error handling must be implemented in the task itself (the job each ). You need to make sure the task you are creating handles the exception.
Parallel.ForEach will not handle it for you since the exception will be raised not in the thread which is calling the Parallel.ForEach.
Alternative is to use Task<T>.

C# ThreadPool QueueUserWorkItem Exception Handling

Whenever a thread in my ThreadPool throws an exception, my code seems to be stuck at the catch block inside the thread function. How do I get the exception back to the main thread?
The best practice is that your background threads should not throw exceptions. Let them handle their exceptions on their own.
Ideally you should wrap the code in your method that executes on a thread in a try-catch block and handle the exception in the catch block. Do not re-throw it from the catch block.
Read this for more details. http://www.albahari.com/threading/#_Exception_Handling
If you want to update the UI from background thread you can do that by using Control.InvokeRequired property and Control.Invoke method. See the MSDN links for details and examples.
It's not possible to transfer exception from a thread to another one. What can you do is to built some synchronization mechanism to transfer exception information between threads and then throw a new exception from the target thread something like:
class Program
{
Exception _savedException = null;
AutoResetEvent _exceptionEvent = new AutoResetEvent(false);
static void Main(string[] args)
{
Program program = new Program();
program.RunMain();
}
void RunMain()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod));
while (true)
{
_exceptionEvent.WaitOne();
if (_savedException != null)
{
throw _savedException;
}
}
}
void ThreadMethod(object contxt)
{
try
{
// do something that can throw an exception
}
catch (Exception ex)
{
_savedException = ex;
_exceptionEvent.Set();
}
}
}
If you have a Win form application things are much simpler. In the catch clause of your thread use Invoke (or BeginInvoke) method of your form, providing it with the exception details. In the method launched with Invoke you can rethrow or treat your exception as you want.

Simplest way to do a fire and forget method in C#?

I saw in WCF they have the [OperationContract(IsOneWay = true)] attribute. But WCF seems kind of slow and heavy just to do create a nonblocking function. Ideally there would be something like static void nonblocking MethodFoo(){}, but I don't think that exists.
What is the quickest way to create a nonblocking method call in C#?
E.g.
class Foo
{
static void Main()
{
FireAway(); //No callback, just go away
Console.WriteLine("Happens immediately");
}
static void FireAway()
{
System.Threading.Thread.Sleep(5000);
Console.WriteLine("5 seconds later");
}
}
NB: Everyone reading this should think about if they actually want the method to finish. (See #2 top answer) If the method has to finish, then in some places, like an ASP.NET application, you will need to do something to block and keep the thread alive. Otherwise, this could lead to "fire-forget-but-never-actually-execute", in which case,of course, it would be simpler to write no code at all. (A good description of how this works in ASP.NET)
ThreadPool.QueueUserWorkItem(o => FireAway());
(five years later...)
Task.Run(() => FireAway());
as pointed out by luisperezphd.
For C# 4.0 and newer, it strikes me that the best answer is now given here by Ade Miller: Simplest way to do a fire and forget method in c# 4.0
Task.Factory.StartNew(() => FireAway());
Or even...
Task.Factory.StartNew(FireAway);
Or...
new Task(FireAway).Start();
Where FireAway is
public static void FireAway()
{
// Blah...
}
So by virtue of class and method name terseness this beats the
threadpool version by between six and nineteen characters depending on
the one you choose :)
ThreadPool.QueueUserWorkItem(o => FireAway());
For .NET 4.5:
Task.Run(() => FireAway());
To add to Will's answer, if this is a console application, just throw in an AutoResetEvent and a WaitHandle to prevent it exiting before the worker thread completes:
Using System;
Using System.Threading;
class Foo
{
static AutoResetEvent autoEvent = new AutoResetEvent(false);
static void Main()
{
ThreadPoolQueueUserWorkItem(new WaitCallback(FireAway), autoEvent);
autoEvent.WaitOne(); // Will wait for thread to complete
}
static void FireAway(object stateInfo)
{
System.Threading.Thread.Sleep(5000);
Console.WriteLine("5 seconds later");
((AutoResetEvent)stateInfo).Set();
}
}
An easy way is to create and start a thread with parameterless lambda:
(new Thread(() => {
FireAway();
MessageBox.Show("FireAway Finished!");
}) {
Name = "Long Running Work Thread (FireAway Call)",
Priority = ThreadPriority.BelowNormal
}).Start();
By using this method over ThreadPool.QueueUserWorkItem you can name your new thread to make it easier for debugging. Also, don't forget to use extensive error handling in your routine because any unhandled exceptions outside of a debugger will abruptly crash your application:
The recommended way of doing this when you are using Asp.Net and .Net 4.5.2 is by using QueueBackgroundWorkItem. Here is a helper class:
public static class BackgroundTaskRunner
{
public static void FireAndForgetTask(Action action)
{
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2 required
{
try
{
action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
/// <summary>
/// Using async
/// </summary>
public static void FireAndForgetTask(Func<Task> action)
{
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2 required
{
try
{
await action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
}
Usage example:
BackgroundTaskRunner.FireAndForgetTask(() =>
{
FireAway();
});
or using async:
BackgroundTaskRunner.FireAndForgetTask(async () =>
{
await FireAway();
});
This works great on Azure Web Sites.
Reference: Using QueueBackgroundWorkItem to Schedule Background Jobs from an ASP.NET Application in .NET 4.5.2
Calling beginInvoke and not catching EndInvoke is not a good approach. Answer is simple:
The reason that you should call EndInvoke is because the results of the invocation (even if there is no return value) must be cached by .NET until EndInvoke is called. For example if the invoked code throws an exception then the exception is cached in the invocation data. Until you call EndInvoke it remains in memory. After you call EndInvoke the memory can be released. For this particular case it is possible the memory will remain until the process shuts down because the data is maintained internally by the invocation code. I guess the GC might eventually collect it but I don't know how the GC would know that you have abandoned the data vs. just taking a really long time to retrieve it. I doubt it does. Hence a memory leak can occur.
More can be found on http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx
Almost 10 years later:
Task.Run(FireAway);
I would add exception handling and logging inside FireAway
The simplest .NET 2.0 and later approach is using the Asynchnonous Programming Model (ie. BeginInvoke on a delegate):
static void Main(string[] args)
{
new MethodInvoker(FireAway).BeginInvoke(null, null);
Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
}
private static void FireAway()
{
Thread.Sleep(2000);
Console.WriteLine("FireAway: " + Thread.CurrentThread.ManagedThreadId );
}
The simplest way to do fire-and-forget is to use the discard pattern:
_ = MyFireAndForgetTask(myParameters);
This notifies your method that the result of your Task will not be needed and execution of the thread is not stalled.
Please note that the Task must call Task.Run within it to be asynchronous using this pattern. Using our previous method as an example:
Task MyFireAndForgetTask(myParameters)
{
return Task.Run(/* put Task, Func<T>, or Action here*/);
}
If this step is ignored, the Task will run synchronously and will not behave as expected.
Furthermore the assignment pattern can be used. This is useful for when the method runs until the last line but hangs until the Task is finished. We will utilize Task.Wait() for this. Using our previous method as an example:
void MyCustomEventHandler(object sender, EventArgs args)
{
/* perform some code here */
var myTask = MyFireAndForgetTask(myParameters);
/* perform some more code here; thread is not blocked */
/// Hang the method until the Task is completed.
/// using "await myTask;" is equivalent.
myTask.Wait();
}
This will perform a fire-and-forget-till-completion, which is mandatory on some platforms (i.e. ASP.NET).
If you want to test in Console keep in mind that Console.ReadKey() or something like that is needed before Console loses its thread by Press any key to continue ...
public static void Main()
{
Task.Factory.StartNew(async () =>
{
await LongTaskAsync();
}, TaskCreationOptions.LongRunning).ConfigureAwait(false);
Console.WriteLine("Starts immediately");
Console.ReadKey();
}
static async Task LongTaskAsync()
{
await Task.Delay(5000);
Console.WriteLine("After 5 seconds delay");
}

Categories