Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run a Task on a new thread and immediately return to the caller?

For the last few months I have been reading about async-await in C# and how to properly use it.

For the purpose of a laboratory exercise, I am building a small Tcp server that should serve clients that connect to it. The program is a console application.

I use a while loop to wait for connections like so:

while (!_isStopRequested)
{
       TcpClient client = await _tcpListener.AcceptTcpClientAsync();

       await Task.Factory.StartNew(() => ProcessClientAsync(client), TaskCreationOptions.LongRunning);
} 

So, until now the method ProcessClientAsync I made was marked as async void and I would just call it ProcessClientAsync(client) and the call would immediately return to the caller. However I read on the internet that it is a poor decision to use it unless it is for an event. So I changed the definition to async Task.

Ok, but without an await, I get a warning in Visual studio "Because this call is not awaited, the current method continues to run before the call is completed".

And once I used await ProcessClientAsync(client), the code doesn't work. I can connect only one client, and then the caller "waits" for ProcessClientAsync to return. However, that method has a infinite loop and will not return, but I need the while loop to continue processing the next client.

Googling I came up to this thread: How to safely call an async method in C# without await

I guess the questions are pretty much the same, except that when I use await ProcessClientAsync, I want it to return to the caller immediately, which it doesn't do, I know because running the second client, while the first is still running, the second client doesn't connect.

So I did this:

await Task.Factory.StartNew(() => ProcessClientAsync(client), TaskCreationOptions.LongRunning);

But since ProcessClientAsync has to return a Task, I am not sure if this is Ok to do?

That would be one question.

The other would be: how could I call a async Task method that will run forever, and have the call return to the caller immediately so the while loop can continue and the tcp listener can continue to accept the next client?

Thank you.

Sorry if it is a repetition, or unclear what I am asking.

like image 200
Marin Avatar asked Jun 07 '15 18:06

Marin


People also ask

Does Task run () create a new thread ?!?

Note: Just using a Task in . NET code does not mean there are separate new threads involved. Generally when using Task. Run() or similar constructs, a task runs on a separate thread (mostly a managed thread-pool one), managed by the .

Does Task delay start a new thread?

Task. Delay does not create new Thread, but still may be heavy, and no guaranties on order of execution or being precise about deadlines.

How do you run a method in a new thread dotnet?

Threading; public class Work { public static void Main() { // Start a thread that calls a parameterized static method. Thread newThread = new Thread(Work. DoWork); newThread. Start(42); // Start a thread that calls a parameterized instance method.

Does Task run block thread?

This is because Task. Run will still block a thread from thread pool the entire time until it finishes the method.


1 Answers

For the purpose of a laboratory exercise, I am building a small Tcp server that should serve clients that connect to it. The program is a console application.

Well, the very first thing I'd recommend is to try a simpler exercise. Seriously, an asynchronous TCP server is one of the most complex applications you can choose. The vast majority of developers using async are working on apps two orders of magnitude simpler.

Instead of that, try writing a UI app that will download a file from a URL. That's a more realistic starting point for learning async.

But since ProcessClientAsync has to return a Task, I am not sure if this is Ok to do?

I'd recommend using Task.Run instead of Task.Factory.StartNew. Task.Run is aware of async methods, whereas StartNew is not.

However, that's assuming that kicking off a thread is the correct operation in the first place. Most TCP/IP server applications are not CPU-bound, so I question the use of multiple threads here. It's entirely possible to have a fully-asynchronous TCP/IP server that only uses the thread pool for its async method continuations.

The other would be: how could I call a async Task method that will run forever, and have the call return to the caller immediately so the while loop can continue and the tcp listener can continue to accept the next client?

You just don't await the Task it returns:

Task.Run(() => ProcessClientAsync(client));

However, the compiler will complain here, because this code is almost certainly wrong. If you ignore the returned task, then you're ignoring any exceptions from that code.

A common pattern in TCP/IP server apps is to maintain a small collection of state for each connected client. That's a natural place to put both the socket object itself and the task that represents the handling of that socket connection:

var clientState = new ClientState(client);
clientState.Task = Task.Run(() => ProcessClientAsync(client));

These client states are then stored in a list/dictionary. How exactly you do this is of course up to you.

like image 82
Stephen Cleary Avatar answered Sep 28 '22 00:09

Stephen Cleary