Reputation: 14297
I have following Synchronous an Asynchronous methods
public static void Info(string message)
{
if (_logTypes.Contains(InfoLogType))
{
string applicationName, methodName, className;
ReflectClassAndMethod(out applicationName, out className, out methodName);
Write(applicationName, "Info", className, methodName, message);
}
}
public static async void InfoAsync(string message)
{
if (_logTypes.Contains(InfoLogType))
{
string applicationName, methodName, className;
ReflectClassAndMethod(out applicationName, out className, out methodName);
await WriteAsync(applicationName, "Info", className, methodName, message);
}
}
Further my write methods are like this:
private static void Write(string applicationName, string logType, string className, string methodName, string message)
{
// construct me _event object
var collection = Database.GetCollection<Event>(typeof(Event).Name);
collection.InsertOne(_event);
}
private static Task WriteAsync(string applicationName, string logType, string className, string methodName, string message)
{
// construct my _event object
var collection = Database.GetCollection<Event>(typeof(Event).Name);
await collection.InsertOneAsync(_event);
}
Using a test console app I call these synchronous and asynchronous method as below:
static void Main(string[] args)
{
Log.Info("Synchronous write");
Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
}
This will insert two documents into my Mongo Collection. but if I comment the synchronous call out, nothing will be inserted.
static void Main(string[] args)
{
//Log.Info("Synchronous write");
Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
}
This will insert nothing. Why is that?
Upvotes: 0
Views: 552
Reputation: 7445
Because your application terminates before the logger is finished.
You can simply await
the call:
static void Main(string[] args)
{
//Log.Info("Synchronous write");
await Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
}
Or, because this doesn't work in Main
, read that: Can't specify the 'async' modifier on the 'Main' method of a console app
You can simply Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture)).Wait();
EDIT:
async Main
is now available when using C# 7.1: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md
So just use static async Task Main(string[] args)
Upvotes: 2
Reputation: 10851
First, I would try to avoid async void InfoAsync
.
Prefer async Task methods over async void methods
Instead you should try something like this:
public static Task InfoAsync(string message)
{
if (_logTypes.Contains(InfoLogType))
{
string applicationName, methodName, className;
ReflectClassAndMethod(out applicationName, out className, out methodName);
return WriteAsync(applicationName, "Info", className, methodName, message);
}
}
This has several upsides. First, there's no reason to await WriteAsync
. Read more about it here. It only adds overhead and could affect performance negatively. Secondly, because you return a Task
instead of void
it can be properly awaited by the caller instead.
And await
it in your main instead. Since it's main you cannot use await
, instead you can use WaitAndUnwrapException().
static void Main(string[] args)
{
//Log.Info("Synchronous write");
var task = Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
task.WaitAndUnwrapException();
// or
// task.Wait();
// Now the application won't finish before the async method is done.
}
What if I have to use this Log.InfoAsync in any other methods of my WebAPP, which are not async methods?
Then they should use Info
instead of InfoAsync
. If possible, try to use async
all the way. If it's not possible, then you should consider if you really add anything by using the async
method. When you await
it you will still block the thread, and if you're using .Result
there's a chance of deadlock. So, in my opinion there's really few cases when you want to use an async
method in an synchronous context.
Further interesting reading: Should Task.Wait be deprecated?
Upvotes: 3