0

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?

Mafii
  • 7,227
  • 1
  • 35
  • 55
HaBo
  • 13,999
  • 36
  • 114
  • 206
  • 1
    agreed. I find the question properly asked, and with an actual problem and possible solution. http://meta.stackexchange.com/questions/135/encouraging-people-to-explain-downvotes – smoksnes Aug 12 '16 at 08:24
  • @smoksnes but that questions helps users who are below 2k reputation. not for one like me... :( – HaBo Aug 12 '16 at 08:33

2 Answers2

3

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?

Community
  • 1
  • 1
smoksnes
  • 10,509
  • 4
  • 49
  • 74
  • What if I have to use this Log.InfoAsync in any other methods of my WebAPP, which are not async methods? – HaBo Aug 12 '16 at 07:57
  • 1
    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. – smoksnes Aug 12 '16 at 08:00
  • So that means async method are supposed to be called only from async methods but not from a synchronous methods? – HaBo Aug 12 '16 at 08:05
  • 2
    @HaBo the problem is that you didnt show us the full situation I assume. You can use asynchronous methods in synchronous calls, but if you want to wait for the result its blocking. In your example/test you have to await/wait for the result because the application would terminate before its finished. But if the code runs on a webserver - just run the task as smoksnes described, but dont await/wait for the result. Logging will take place if the process doesnt get shut down. – Mafii Aug 12 '16 at 08:09
2

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)

Mafii
  • 7,227
  • 1
  • 35
  • 55
  • await might not be an option because i will be using this logger from various parts of the application which can be synchronous or asynchronous methods and tried .Wait on the same test console app, no difference. – HaBo Aug 12 '16 at 07:53
  • So `.Wait()` still doesn't return the expected result? – Mafii Aug 12 '16 at 07:56
  • I had to change return type to TASK for InfoAsync then I could use .Wait() and YES .Wait() did not insert the record. – HaBo Aug 12 '16 at 08:01
  • You were right. it works. But now .Wait() isn't that making this a synchronous call? – HaBo Aug 12 '16 at 08:03
  • .Wait() is a blocking call onto a synchronous method. You can not work asynchronous in a synchronus environnment. This is as close as it gets.But be sure to read up what .Wait() does exactly. Whats your reason for wanting async onto logging in the first place? – Mafii Aug 12 '16 at 08:07
  • 1
    @HaBo, yes. You will block the thread, and therefore there's really no reason to call `Log.InfoAsync` over `Log.Info`. – smoksnes Aug 12 '16 at 08:14