0

I have gone through several post for converting existing synchronous method to asynchronous. So based on what I have read I am converting the synchronous method to asynchronous like below

Synchronous

public class SomeClass
{
    public int DoWork()
    {
        return DoLongRunningWork();
    }

    public int DoLongRunningWork()
    {

        Thread.Sleep(1000);
        return 1;
    }
}

I converted this to Asynchronous version like below

public class SomeClass
{
    public async Task<int> DoWorkAsync()
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        return await DoLongRunningWorkAsync();
    }

    public Task<int> DoLongRunningWorkAsync()
    {
        Thread.Sleep(1000);
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        return Task.Run(() => 1);
    }
}

I am calling this from Main() Method

    static void Main()
    {           
        Someclass worker = new SomeClass();
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

        var result = worker.DoWorkAsync().GetAwaiter().GetResult();

        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine(result);

        Console.ReadKey();
    }
  1. Is this a correct way to convert synchronous to asynchronous method?
  2. I was expecting Main method's ManagedThreadId will be different than DoWorkAsync and DoLongRunningWorkAsync method's ManagedThreadId. But they are same, why?

I used Thread.Sleep() just to simulate the long running method. As per the suggestions below i should have used Task.Delay() to avoid any confusion.
I dont think i have to put my actual business logic here to understand the concept of async.

As per Stephen Cleary
1> Identify the naturally-asynchronous operations your code is doing.
2>Change the lowest-level API calls to invoke asynchronous APIs (with await) instead of synchronous APIs.

However none of the .Net Libarary methods im using inside LongRunningMethod are naturally-asynchronous or awaitable and also LongRunningMethod is not doing any I/O operation.

Requirement

I have Web API which takes JSON string as input. The JSON needs to be transformed into some C# object. For transformation I am building a C# library which takes JSON string as input and then Transform that JSON string into C# objects based on some rules. The library may take time ( few milliseconds) for Transformation, during this I DO NOT want Web API's main thread to block. The main thread should be free to take any other request while the transformation is going on background thread (some other thread).

public class MyWebApi: ApiController
{

    private ILib _myLibrary

    public MyWebApi(ILib myLibrary)
    {
       _myLibrary = myLibrary
    }

    publi async Task<SomeObject> Transform(string jsonString)
    {
          // i want to make LongRunningMethod() method awaitable
          // or at least it needs to execute on different thread  so that 
          // main thread will be free.

         var result = await _myLibrary.LongRunningMethod(jsonString);
         return result;
    }
}

publi class MyLibrary:ILib
{


   // A method that does Transformation
   public async Task<SomeObject> LongRunningMethod(string jsonString)
   {
       var result = new SomeObject();

       // parse jsonString here
       // and based on some complex rules create SomeObject
       // this operation may takes time (lets say few milliseconds)
       // i may call some other private methods here or public methods from other library          
       return result
   }    
}
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
LP13
  • 30,567
  • 53
  • 217
  • 400
  • 1
    `DoLongRunningWorkAsync` is completely synchronous... So there is really no "conversion from sync to async" in your post. – Alexei Levenkov Aug 17 '16 at 23:13
  • 1
    Without a real code example, it's impossible to offer specific advice. For sure, the above code is pointless. The `DoWorkAsync()` method has no apparent need to `await`, so making it `async` isn't useful. The `DoLongRunningWorkAsync()` inserts a _synchronous_ delay into the call. Why you sleep at all I have no idea, but doing so at that point doesn't make any sense. But given the lack of a real example, I can't tell if you just messed up your question, or if your real-world code is similarly broken. – Peter Duniho Aug 17 '16 at 23:19

1 Answers1

2

based on what I have read I am converting the synchronous method to asynchronous like below

The best way to convert synchronous methods to asynchronous (as I describe in my async brownfield article) is the following:

  1. Identify the naturally-asynchronous operations your code is doing. This is anything that is not running CPU code, e.g., I/O.
  2. Change the lowest-level API calls to invoke asynchronous APIs (with await) instead of synchronous APIs.
  3. Note the compiler warning and change your calling method to be async with a proper (Task/Task<T>) return type. Also add an Async suffix.
  4. Change calling methods to use await, and repeat step (3).

When you run into problems with step (3), check out my articles on async OOP, async MVVM (if applicable), and async brownfield.

Applying these to your code:

  1. Identify the naturally-asynchronous operations your code is doing. This is anything that is not running CPU code, e.g., I/O.

Your lowest-level method has a call to Thread.Sleep, which is not running code. The asynchronous equivalent is Task.Delay.

  1. Change the lowest-level API calls to invoke asynchronous APIs (with await) instead of synchronous APIs.
public int DoLongRunningWork()
{
  await Task.Delay(1000);
  Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
  return 1;
}
  1. Note the compiler warning and change your calling method to be async with a proper (Task/Task<T>) return type. Also add an Async suffix.
public async Task<int> DoLongRunningWorkAsync()
{
  await Task.Delay(1000);
  Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
  return 1;
}
  1. Change calling methods to use await, and repeat step (3).
public int DoWork()
{
  return await DoLongRunningWorkAsync();
}
  1. Note the compiler warning and change your calling method to be async with a proper (Task/Task<T>) return type. Also add an Async suffix.
public async Task<int> DoWorkAsync()
{
  return await DoLongRunningWorkAsync();
}
  1. Change calling methods to use await, and repeat step (3).

In this case, since Main cannot be async, you'll need to block with GetAwaiter().GetResult(), just like you currently have.

Final result:

public async Task<int> DoWorkAsync()
{
  return await DoLongRunningWorkAsync();
}

public async Task<int> DoLongRunningWorkAsync()
{
  await Task.Delay(1000);
  Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
  return 1;
}

I was expecting Main method's ManagedThreadId will be different than DoWorkAsync and DoLongRunningWorkAsync method's ManagedThreadId. But they are same, why?

"Asynchronous" does NOT mean "runs on a different thread." See my async intro for more details about how async/await works.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810