1

Following is logger call, which is Blocking:

public static void Info(Category category, SubLevel subLevel, object message)
{
    CommonLogger?.Info($"{category}, {(int) subLevel}, {message}");
}

I want to make it Asynchronous execution, therefore I did the following modification, since as per my understanding having Async-Await for void return method, like current one is not a good strategy:

public static void InfoAsync(Category category, SubLevel subLevel, object message)
{
    Task.Run(() => CommonLogger?.Info($"{category}, {(int)subLevel}, {message}"));
}

Code Context -

  • Make the logging run Asynchronously
  • It is Fire and Forget, main business logic proceed separately

My questions are:

  1. Am I correct in making this change ?

  2. Is there still a better way, to do the same ?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
  • 3
    In terms of answering the question in your title - yes, it will now run asychronously. The other 2 questions are too subjective and could be opinion-based. Get it on codereview.stackexchange.com instead. Whether it's on SO or codereview, it'll need more context. –  Mar 31 '16 at 14:10
  • Thanks I will post to CodeReview – Mrinal Kamboj Mar 31 '16 at 14:15
  • *Am I correct in making this change* Depends on what you want to achive. *Is there still a better way, to do the same* Depends on what you want to achieve. – Liam Mar 31 '16 at 14:15
  • To post on code review you need more details. – Liam Mar 31 '16 at 14:16
  • Does this context helps to get a better view – Mrinal Kamboj Mar 31 '16 at 14:18
  • Wonder why down vote, is there something wrong with the question – Mrinal Kamboj Mar 31 '16 at 14:20

3 Answers3

5

Does this modification makes my method run asynchronously?

No, it doesn't. Concurrency and parallelism are two different concepts. People tend to make concurrent and parallel interchangeable, but they really aren't. Your code will run in parallel to other executing units, but by itself the operation isn't asynchronous. If you really wanted to make an asynchronous operation, you'd use async IO to do the writing to the underlying logger.

Am I correct in making this change?

I would say no. This is called the async over sync anti-pattern (see the link for an extensive explanation of what that means). I would avoid this pattern, as it creates a false sense of asynchrony. Your method isn't really naturally asynchronous, true asynchronous methods don't need to use extra thread-pool threads.

Is there still a better way, to do the same?

Yes, simply don't do this. Let the caller of this method explicitly call Task.Run so they know that the said delegate is going to execute on the thread-pool. Or even better, use async IO with the logger implementation. After all, I'm assuming this will probably write to an underlying file or execute some kind of network operation to send the log over the wire. If so, take advantage of true async APIs instead.

Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • You mean directly call the Synchronous version using Task.Run, like: Task.Run(() => Info()); – Mrinal Kamboj Mar 31 '16 at 14:23
  • 2
    @MrinalKamboj Yes. – Yuval Itzchakov Mar 31 '16 at 14:24
  • Running in parallel, in this context, is also running async. Your answer is terrible. –  Mar 31 '16 at 14:31
  • This internally calls Log4Net via Common logging facade, they have buffering mechanism, but did not want that call to be blocking in any way – Mrinal Kamboj Mar 31 '16 at 14:31
  • 2
    @JᴀʏMᴇᴇ No, there is nothing "async" about queueing a delegate on a threadpool thread. Feel free to elaborate on why my answer is terrible, perhaps I can improve it. – Yuval Itzchakov Mar 31 '16 at 14:32
  • 2
    @MrinalKamboj You can use [async appenders](https://www.nuget.org/packages/Log4Net.Async/) for log4net. – Yuval Itzchakov Mar 31 '16 at 14:34
  • 1
    I fully agree that masking this method underneath a Task.Run() but not making that readily known by callers is an antipattern. To add to the discussion, if this were called in an ASP.NET context you're actually reducing the scalability of the application by taking up an extra thread to log something that could be accepting an incoming request. If you want it to be async use async all the way to get the most bang out of it in regards to scale. – davidallyoung Mar 31 '16 at 19:17
1
  1. Yes this now is running asynchronously (in the sense that your method returns without waiting for the logging to complete, not in the meaning explained by Yuval Itzchakov).
  2. Well, why don't you want to change that method to return the Task and make it awaitable for callers, too:

    public static async Task InfoAsync(Category category, SubLevel subLevel, object message)
    {
        await Task.Run(() => CommonLogger?.Info($"{category}, {(int)subLevel}, {message}"));
    }
    

But as you and other commenters pointed out: if you don't need this, it only adds unnecessary complexity (e.g. the compiler builds an unnecessary state machine).

In general, to answer questions like "can it be done better" one needs to know the criteria for "better". It depends on how and under which conditions this will be called and what the requirements are.

René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • 1
    Why add the async await? This seems to have no benefit and makes the execution more complex? – Liam Mar 31 '16 at 14:13
  • 1
    Its Fire and Forget for me, I have no reason to return a task, it just goes and logs details in background – Mrinal Kamboj Mar 31 '16 at 14:13
  • @Liam well that's the thing about "could it be better" questions. How should I know what requirements are there. To make it async doesn't have any drawbacks as far as I can see, so why not offering the possibility to callers? – René Vogt Mar 31 '16 at 14:16
  • ^ This misunderstanding is why it's always best to ask for more context before answering. You end up spinning on the spot until the OP is as clear as possible. –  Mar 31 '16 at 14:17
  • 1
    *To make it async doesn't have any drawbacks* yes it does, it makes the execution (performed by the CLR) considerably more complex and costly. If the Op just wants fire and forget this will simply make it run slower to no benefit – Liam Mar 31 '16 at 14:17
  • @Liam got your point. I just thought about complexity in terms of how to use the code. And I'm no compiler expert, but could imagine that there will be no state machine created for this one-liner, since there is no need to resume the method, but I may be wrong here. – René Vogt Mar 31 '16 at 14:19
  • @ René Vogt Correct no state machine created in original version, so much simpler and efficient implementation – Mrinal Kamboj Mar 31 '16 at 14:20
  • 1
    @MrinalKamboj I actually meant the `async` version, too :) ....guess it's been enough discussion about this minor issue. – René Vogt Mar 31 '16 at 14:22
1

Yes, this is now an ASync method, however, the other two aren't proper questions (as mentioned by JayMee). They are purely how people develop. For example, I would do that, however, Joe Bloggs may not.

Hope this helps.

TechnicalTophat
  • 1,655
  • 1
  • 15
  • 37