16

I'm trying to make code replacement from Thread to Task. The sleep / delay is just representing long running activity.

static void Main(string[] args)
{
    ThreadDoWork();
    TaskDoWork();
}
public static void ThreadDoWork()
{
    using (var dispose = new ThreadDispose())
    {
        dispose.RunAsync();
    }
}
public static async void TaskDoWork()
{
    using (var dispose = new TaskDispose())
    {
        await dispose.RunAsync();
    }
}
public class ThreadDispose : IDisposable
{
    public void RunAsync ()
    {
        ThreadPool.QueueUserWorkItem(state =>
        {
            Thread.Sleep(3000);
        });
    }
    void IDisposable.Dispose()
    {
        File.AppendAllText("D:\\test.txt", "thread disposing");
    }
}
public class TaskDispose : IDisposable
{
    public async Task RunAsync()
    {
        await Task.Delay(3000);
    }
    void IDisposable.Dispose()
    {
        File.AppendAllText("D:\\test.txt", "task disposing");
    }
}

The result after 3 seconds in test.txt is only

thread disposing

What do I need to change in order TaskDispose::Dispose is always executed just like ThreadDispose?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Yuliam Chandra
  • 14,494
  • 12
  • 52
  • 67
  • Do you realize that your ThreadDispose is disposed before it's done working? I guess the task will dispose, too, if you give it time to actually complete. – nvoigt Aug 03 '14 at 16:43
  • yup I realized that, the task concept is kind of new to me, so kindly advice me what to do to replace thread code with task code – Yuliam Chandra Aug 03 '14 at 16:45
  • Try changing `public static async void TaskDoWork()` to `public static async Task TaskDoWork()` and await it in `Main`... – Anthony Chu Aug 03 '14 at 16:47
  • @AnthonyChu, same result, and the `TaskDoWork();` in main is warning `Because this code is not awaited..`, and I tried to just `await TaskDoWork();` and change the `Main` into `static async void Main`, but it compiles error – Yuliam Chandra Aug 03 '14 at 16:50

2 Answers2

24

Let's isolate each piece of code:

public static void ThreadDoWork()
{
    using (var dispose = new ThreadDispose())
    { 
        dispose.RunAsync();
    }
}

public void RunAsync()
{
    ThreadPool.QueueUserWorkItem(state =>
    {
        Thread.Sleep(3000);
    });
}

What you do in this first piece of code is queue work on a threadpool thread. Because you're running this code inside a using scope and it runs asynchronously on a different thread, it disposes immediately. That is why you see the dispose message inside your text file.

public static async void TaskDoWork()
{
   using (var dispose = new TaskDispose())
   {
       await dispose.RunAsync();
   }
}

public class TaskDispose : IDisposable
{
   public async Task RunAsync()
   {
       await Task.Delay(3000);
   }
}

When you await inside your method, what you actually say is something along the lines of: "Execute this code. Because it's asynchronous by nature, I will return control back to the calling method, please call me back once you complete the asynchronous operation".

Your code hits the await keyword and returns control to your Main method. Inside Main, your async method is the last piece of code to execute, hence finishing your application, and not giving a chance for your Dispose method to execute.

If you want it to dispose, you'll have to change the return type from void to Task and explicitly Wait:

public static async Task TaskDoWork()
{
    using (var dispose = new TaskDispose())
    {
       await dispose.RunAsync();
    }
}

And now:

static void Main(string[] args)
{
    ThreadDoWork();
    TaskDoWork().Wait();
}

Side Note:

There are a couple of guidelines that should be followed:

  1. async void is for compatability with event handlers, there are rarely occasions outside that scope where it should be used. Instead, use async Task.

  2. Methods doing asynchoronous operation using TAP (Task Asynchronous Pattern) should end with the Async postfix. TaskDoWork should be TaskDoWorkAsync.

  3. Using Wait on a Task can cause deadlocks. In this particular case it doesn't because a console application doesn't have a SynchronizationContext and uses the threadpools. The recommended approach is to go "async all the way" and use await.

There is great reading material inside the async-await tag wiki, make sure to check it out.

Pang
  • 9,564
  • 146
  • 81
  • 122
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
-1

The both examples has issues. The first, in the thread case, the Dispose method is calling before the thread has ended. In the case of the task, the Dispose method will be call when the task has finished.

But the file only has the text written by the thread case, because it is called synchronously after the call of RunAsync method. And the Task case doesn't write anything because the application is ended before the task has finished.

Test to wait the task to finish with a Thread.Sleep(5000) for example at the end of the Main method, and the task should write in the output file.

danielcaceresm
  • 129
  • 1
  • 3