I have an old piece of code (.NET 3.5) where I run in a separate thread the registration of new TcpClient:
Old code:
public class TcpServer
{
private readonly TcpListener _tcpListener;
public TcpServer(int port)
{
_tcpListener = new TcpListener(IPAddress.Any, port);
}
public void StartListening()
{
_tcpListener.Start();
var t = new Thread(new ThreadStart(AcceptConnections));
t.Start();
}
private void AcceptConnections()
{
while (true)
{
var client = _tcpListener.AcceptTcpClient();
// Some stuff
}
}
}
The goal of this code is to avoid blocking the app while waiting for new clients. I am porting this code to .NET Standard and I wonder whether I have to use the new features of .NET.
I am talking about async/await
and Task
.
Async Thread version
Since this thread will last as long as the whole application, I first considered to simply replace the actual method by the new asynchronous one. But if I do so, I end up with a new thread running an async void
method:
public class TcpServer
{
private readonly TcpListener _tcpListener;
public TcpServer(int port)
{
_tcpListener = new TcpListener(IPAddress.Any, port);
}
public void StartListening()
{
_tcpListener.Start();
var t = new Thread(new ThreadStart(AcceptConnectionsAsync));
t.Start();
}
private async void AcceptConnectionsAsync()
{
while (true)
{
var client = await _tcpListener.AcceptTcpClientAsync();
// Some stuff
}
}
}
It is not recommended to write async void
method except in rare case (like event handler).
Is this case acceptable?
Async task version
As a new option, I run the registration in a Task
:
public class TcpServer
{
private readonly TcpListener _tcpListener;
public TcpServer(int port)
{
_tcpListener = new TcpListener(IPAddress.Any, port);
}
public Task StartListeningAsync()
{
_tcpListener.Start();
return Task.Run(async () => await AcceptConnectionsAsync());
}
private async Task AcceptConnectionsAsync()
{
while (true)
{
var client = await _tcpListener.AcceptTcpClientAsync();
// Some stuff
}
}
}
With this code, as long as I do not await
the AcceptConnectionsAsync
call, the app is not blocked (I simplify the code with a while(true)
but I handle the closure of the app and the end of the listening properly, I can call the await there or even fire-and-forget it).
But as I said, this action is supposed to last as long as the whole application, I think a new thread is more appropriate (in term of architecture at least but also in term of efficiency).
Am I wrong?
Do not change anything to this part of the code
This is the last option I see. I intended to update the code with the Async/Await
because I read that the async/await improves the performance of the TcpListener.
But is that true?
Important information
Here are some information I think can be relevant. Do not hesite to ask for further details.
- The application running this code is a Console App. I know this can be important regarding to async/await context?
- The
AcceptConnections(Async)
method runs a new Task for each new connection (these tasks are short-lived and of type fire-and-forget). - The main thread performs huge calculations. The best solution is the least impacting regarding to the main thread performance.
- There is one other thread that behave like this one (same duration), responsible for listening the data coming from all clients. I intend to apply the same operations as this one.