2

I have a method HandleAcceptedConnection that is under Task.Run() that i want to run asynchronously(in another separate thread). I tried declaring HandleAcceptedConnection as async method and dont call await but it doesnt seem to run asynchronously. I can confirm that I can have Task.Run()(by watching the thread id) under another Task.Run() but is that recommended?

private async void Start_Click(object sender, RoutedEventArgs e)
{
        var task = Task.Run(() =>
        {
            while (isContinue)
            {
                var handler = listener.Accept();
                // handle connection

                Log("Before");
                Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);

                // i want to run method below asynchronously. i want to 
                // wrap it under Task.Run() but i am already under 
                // Task.Run(). i set HandleAcceptedConnection as async. i thought by not 
                // calling await on HandleAcceptedConnection, HandleAcceptedConnection 
                // is asynchronous
                HandleAcceptedConnection(handler); 

                Log("After");

                isContinue = true;
            }
        });
        await task;
}

private async Task HandleAcceptedConnection(Socket handler)
{
    Log("ThreadId HandleAcceptedConnection " + Thread.CurrentThread.ManagedThreadId);
    Log("Under HandleAcceptedConnection");
    Thread.Sleep(10000);   
}

When i run this, logs says

Before
Under HandleAcceptedConnection
After

i want

Before
After
Under HandleAcceptedConnection

i want HandleAcceptedConnection to be run asynchronously. Should i wrap it under another Task.Run or it is already asynchronous?

svick
  • 236,525
  • 50
  • 385
  • 514
Syaiful Nizam Yahya
  • 4,196
  • 11
  • 51
  • 71
  • 5
    Your method will run synchronously because it contains no `await` statement. But even if it *did* run asynchronously, that would mean you do not have any guarantees at all as to which order "After" and "Under" will appear in. If you *need* "After" to appear before "Under", just move the method call to the end. What are you trying to achieve here? – Jon Feb 10 '14 at 11:34
  • 3
    I think you need to read into async/await/Tasks more deeply. The main thing to understand: async/await/Task is not necessarily equal to multithreading. – Krumelur Feb 10 '14 at 11:41
  • @Jon "Before" "after" "Under" is just designation to indicate whether HandleAcceptedConnection is run synchronously or asynchronous. A better way to know whether HandleAcceptedConnection is run async or not is by monitoring the thread(which i just added). All i want is to run HandleAcceptedConnection in another thread/async from 'the caller above'. – Syaiful Nizam Yahya Feb 10 '14 at 12:03
  • 1
    @publicENEMY now replace `Thread.Sleep(10000); ` with `await Task.Delay(10000);` and try again. BTW: Use it before *Logging* to be able to see that it will run *after* `Log("after")` – L.B Feb 10 '14 at 12:06
  • @L.B That does it. Ive also put Thread.CurrentThread.ManagedThreadId before and after Task.Delay() and it seems that before and after Task.Delay() uses different thread. It seems that Task.Delay() caused the method to use different thread. – Syaiful Nizam Yahya Feb 10 '14 at 12:38

2 Answers2

2

Did you try

private async Task HandleAcceptedConnection(Socket handler)
{
    Thread.Sleep(1000);
    Log("Under HandleAcceptedConnection");
}

Because doing something on another thread doesn't mean it'll be delayed.

Max
  • 12,622
  • 16
  • 73
  • 101
Tarec
  • 3,268
  • 4
  • 30
  • 47
  • 2
    Better use `Task.Delay()` - it won't waste a thread by blocking it. And then you will have to await it - otherwise it does not make a lot of sense to make the method `async` in the first place. – Krumelur Feb 10 '14 at 11:40
  • 1
    @Krumelur Not only *better*. He/she must use `await Task.Delay(1000);` to get the desired effect in question. – L.B Feb 10 '14 at 11:49
  • 1
    @L.B I did not understand what problem he wants to solve, so I'm not sure what the desired effect is :-) – Krumelur Feb 10 '14 at 11:50
  • 1
    @Krumelur OP wants not to wait the `HandleAcceptedConnection` – L.B Feb 10 '14 at 11:51
  • 1
    Exactly. If he does not want to wait, why make it async to begin with? What I'm trying to say: the asker is trying to solve something that does not sound like it has been thought through properly and is a misinterpretation and mixture of multithreading, ATPL, sync and async code. – Krumelur Feb 10 '14 at 11:54
  • @Tarec Actually, not only I put Thread.Sleep() theres a lot of things going on under HandleAcceptedConnection. I did put Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId); and both have the same thread id. – Syaiful Nizam Yahya Feb 10 '14 at 11:55
  • 1
    @Krumelur of course, but as I said in my previous comment. changing a single line makes it to work even though is it not a good option. – L.B Feb 10 '14 at 11:56
  • @Krumelur Exactly. This multithreading, ATPL, sync and async really makes my head go haywire. Since it seems you understand the problem well, what do you suggest? Thanks. – Syaiful Nizam Yahya Feb 10 '14 at 12:10
  • 1
    Start from here, for instance: http://stackoverflow.com/questions/10285159/difference-between-the-tpl-async-await-thread-handling – Krumelur Feb 10 '14 at 13:12
1

You should be using AcceptTcpClientAsync, then you won't need extra threads. Check this answer for an example. Don't use a synchronous API when there is a naturally asynchronous version of it available.

Updated to address the comment. Nothing prevents you from using Task.Run from inside Task.Run, you code might look like this (untested):

private async void Start_Click(object sender, RoutedEventArgs e)
{
    var connectionTasks = new List<Task>();

    Func<Task> handleConnection = async () =>
    {
        var connectionTask = Task.Run(() => HandleAcceptedConnection(handler));
        connectionTasks.Add(connectionTask);
        await connectionTask;
        connectionTasks.Remove(connectionTask);
    };

    var task = Task.Run(() =>
    {
        while (isContinue)
        {
            var handler = listener.Accept();
            // handle connection

            Log("Before");
            Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);

            var connectionTask = handleConnection();

            Log("After");

            isContinue = true;
        }
    });
    await task;
}
Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • There are no AcceptTcpClientAsync in the network library that i used. In fact, all exposed method is synchronous in the network library that i used. – Syaiful Nizam Yahya Feb 10 '14 at 11:57
  • 1
    @publicENEMY, I see. You should've clarified this in your question. I now recalled there was another question where you mentioned a 3rd party sockets-like library with async support, but tomorrow I won't remember that :) – noseratio Feb 10 '14 at 12:02
  • LOL. Sorry. I dont want to mention anything about socket or networking because the question is really about async stuff. – Syaiful Nizam Yahya Feb 10 '14 at 12:06
  • Thanks for the clarification that i can run Task.Run() under another Task.Run(). – Syaiful Nizam Yahya Feb 10 '14 at 12:32
  • 1
    @publicENEMY, no problem. Note you need `async` before the lambda only if you use `await` inside it - as usual. – noseratio Feb 10 '14 at 12:45
  • I noticed that there are await in front of HandleAcceptedConnection. Wouldnt that make the calls wait until HandleAcceptedConnection finish before continue? – Syaiful Nizam Yahya Feb 10 '14 at 15:22
  • 1
    @publicENEMY, indeed, in your case each new connection means a new pool thread, so you should not `await` the result of `Task.Run`. You still need to track connections though. I've updated the answer to show how it can possibly be done. – noseratio Feb 10 '14 at 20:10