This question already has answers here:
await vs Task.Wait - Deadlock?
(3 answers)
Closed 7 years ago.
I'm communicating with another process via named pipes. The pipe server is implemented in C# and the client is written in C. The server is a WPF application.
I need to create a NamedPipeServerStream and wait (synchronously) up to 1 second for the client to connect. And then I need to know whether the client connected.
As NamedPipeServerStream's only way to cancel/timeout a wait for the client to connect is via its asynchronous WaitForConnectionAsync method - which takes a CancellationToken - I've implemented what I believe is a synchronous wait like so:
public bool WaitOneSecondForClientConnect()
{
bool result = false;
try
{
result = WaitForConnectionAsyncSyncWrapper().Result;
}
catch (AggregateException e)
{
log.Write("Error waiting for pipe client connect: " + e.InnerException.Message);
}
return result;
}
private async Task<bool> WaitForConnectionAsyncSyncWrapper()
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}
The pipe is defined like so: NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
The WaitOneSecondForClientConnect() function runs on the UI thread.
What should make it synchronous is accessing the async WaitForConnectionAsyncSyncWrapper() function's Result property on the Task<bool> it returns. In order to access the Result, the async function must have fully returned, and it can't execute the return pipe.IsConnected line until the function is resumed after await pipe.WaitForConnectionAsync(cts.Token); has completed. At least that's my understanding.
So the problem: although the client program says it's opened my server's pipe (which has already been created of course before the code above executes), WaitOneSecondForClientConnect() never returns. If I break into the server, it's on this line: result = WaitForConnectionAsyncSyncWrapper().Result;.
So I guess it's waiting for the Task's Result to be available, which should be the value of pipe.IsConnected if the client connected within 1 second, or it should throw an AggregateException when I access it if the await has completed because the token has been cancelled (after 1 second). But it's just hanging completely.
On the other hand, if I cancel the token before it starts e.g. by putting a Thread.Sleep(2000); right before calling await pipe.WaitForConnectionAsync(cts.Token);, then the connection is cancelled successfully (I think it doesn't even try to start because the token is already cancelled) - accessing the Result property throws an AggregateException, etc...
A few things to note.
If I replace the content of WaitOneSecondForClientConnect() with a
standard synchronous pipe.WaitForConnection();, it works every time -
i.e. the client connects, and the function returns.
In a test program I wrote to initially get this async/sync stuff working, connecting in this synchronous-asynchronous way works every time. It's a console program as opposed to my real program which is WPF. The relevant code of the test program is listed below.
The code posted above has actually worked a couple of times, and failed maybe 30-40 times.
If my client doesn't open my pipe, my "real" code still hangs, whereas my test code waits the specified time period and then prints "Connection failed." as expected (see below) - which is exactly the behaviour that should be happening in my real code.
The test code that works:
var pipe = new NamedPipeServerStream("SemiUsefulPipe_" + pid.ToString() + "ctest", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
// ... dll containing pipe client is injected in client process at this point.
try
{
var result = ConnectAsync(pipe).Result;
}
catch (AggregateException)
{
Console.WriteLine("Connection failed.");
}
...
private static async Task<bool> ConnectAsync(NamedPipeServerStream pipe)
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}
You can't mix async and non-async methods like this. What's happening is that your WaitOneSecondForClientConnect method is waiting for WaitForConnectionAsyncSyncWrapper method to complete. But that guy needs its calling thread to be free so it can rehydrate the original context. So you've just created a deadlock. For details, see https://msdn.microsoft.com/en-us/magazine/jj991977.aspx.
Instead, you need to go async all the way down.
public async Task<bool> WaitOneSecondForClientConnect()
{
bool result = false;
try
{
result = await WaitForConnectionAsyncSyncWrapper();
}
catch (Exception e)
{
log.Write("Error waiting for pipe client connect: " + e.Message);
}
return result;
}
Related
I'm currently working on ASP.NET Core WebApp, which consist of web server and two long-running services– TCP Server (for managing my own clients) and TCP Client (integration with external platform).
Both of services are running alongside web sever– I achieved that, by making them inherit from BackgroundService and injecting to DI in this way:
services.AddHostedService(provider => provider.GetService<TcpClientService>());
services.AddHostedService(provider => provider.GetService<TcpServerService>());
Unfortunately, while development I ran into weird issue (which doesn't let me sleep at night so at this point I beg for your help). For some reason async code in TcpClientService blocks execution of other services (web server and tcp server).
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace ClientService.AsyncPoblem
{
public class TcpClientService : BackgroundService
{
private readonly ILogger<TcpClientService> _logger;
private bool Connected { get; set; }
private TcpClient TcpClient { get; set; }
public TcpClientService(ILogger<TcpClientService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
if (Connected)
{
await Task.Delay(100, stoppingToken); // check every 100ms if still connected
}
else
{
TcpClient = new TcpClient("localhost", 1234);
HandleClient(TcpClient); // <-- Call causing the issue
_logger.Log(LogLevel.Debug, "After call");
}
}
catch (Exception e)
{
// log the exception, wait for 3s and try again
_logger.Log(LogLevel.Critical, "An error occured while trying to connect with server.");
_logger.Log(LogLevel.Critical, e.ToString());
await Task.Delay(3000, stoppingToken);
}
}
}
private async Task HandleClient(TcpClient client)
{
Connected = true;
await using var ns = client.GetStream();
using var streamReader = new StreamReader(ns);
var msgBuilder = new StringBuilder();
bool reading = false;
var buffer = new char[1024];
while (!streamReader.EndOfStream)
{
var res = await streamReader.ReadAsync(buffer, 0, 1024);
foreach (var value in buffer)
{
if (value == '\x02')
{
msgBuilder.Clear();
reading = true;
}
else if (value == '\x03')
{
reading = false;
if (msgBuilder.Length > 0)
{
Console.WriteLine(msgBuilder);
msgBuilder.Clear();
}
}
else if (value == '\x00')
{
break;
}
else if (reading)
{
msgBuilder.Append(value);
}
}
Array.Clear(buffer, 0, buffer.Length);
}
Connected = false;
}
}
}
Call causing the issue is located in else statement of ExecuteAsync method
else
{
TcpClient = new TcpClient("localhost", 1234);
HandleClient(TcpClient); // <-- Call causing the issue
_logger.Log(LogLevel.Debug, "After call");
}
The code reads properly from the socket, but it blocks initialization of WebServer and TcpServer. Actually, even log method is not being reached. No matter if I put await in front of HandleClient() or not, the code behaves the same.
I've done some tests, and I figured out that this piece of code is not blocking anymore ("After call" log shows up):
else
{
TcpClient = new TcpClient("localhost", 1234);
await Task.Delay(1);
HandleClient(TcpClient); // <- moving Task.Delay into HandleClient also works
_logger.Log(LogLevel.Debug, "After call");
}
This also works like a charm (if I try to await Task.Run(), it will block "After call" log, but rest of app will start with no problem):
else
{
tcpClient = new TcpClient("localhost", 6969);
Connected = true;
Task.Run(() => ReceiveAsync(tcpClient));
_logger.Log(LogLevel.Debug, "After call");
}
There is couple more combinations which make it work, but my question is– why other methods work (especially 1ms delay- this completely shut downs my brain) and firing HandleClient() without await doesn't? I know that fire and forget may not be the most elegant solution, but it should work and do it's job shouldn't it? I searched for almost a month, and still didn't find a single explanation for that. At this point I have hard time falling asleep at night, cause I have no one to ask and can't stop thinking about that..
Update
(Sorry for disappearing for over a day without any answers)
After many many hours of investigation, I started debugging once again. Every time I would hit while loop in HandleClient(), I was losing control over debugger, program seemed to continue to work, but it would never reach await streamReader.ReadAsync(). At some point I decided to change condition in the while loop to true (I have no idea why I didn't think of trying it before), and everything began to work as expected. Messages would get read from tcp socket, and other services would fire up without any issues.
Here is piece of code causing issue
while (!streamReader.EndOfStream) <----- issue
{
var res = await streamReader.ReadAsync(buffer, 0, 1024);
// ...
After that observation, I decided to print out the result of EndOfStream before reaching the loop, to see what happens
Console.WriteLine(streamReader.EndOfStream);
while (!streamReader.EndOfStream)
{
var res = await streamReader.ReadAsync(buffer, 0, 1024);
// ...
Now the exact same thing was happening, but before even reaching the loop!
Explanation
Note:
I'm not senior programmer, especially when it comes to dealing with asynchronous TCP communication so I might be wrong here, but I will try to do my best.
streamReader.EndOfStream is not a regular field, it is a property, and it has logic inside it's getter.
This is how it looks like from the inside:
public bool EndOfStream
{
get
{
ThrowIfDisposed();
CheckAsyncTaskInProgress();
if (_charPos < _charLen)
{
return false;
}
// This may block on pipes!
int numRead = ReadBuffer();
return numRead == 0;
}
}
EndOfStream getter is synchronous method. To detect whether stream has ended or not, it calls ReadBuffer(). Since there is no data in the buffer yet and stream hasn't ended, method hangs until there is some data to read. Unfortunately it cannot be used in asynchronous context, it will always block (unfortunately because it seems to be the only way to instantly detect interrupted connection, broken cable or end of stream).
I don't have finished piece of code yet, I need to rewrite it and add some broken connection detection. I will post my solution I soon as I finish.
I would like to thank everyone for trying to help me, and especially #RoarS. who took biggest part in discussion, and spent some of his own time to take a closer look at my issue.
This is poorly documented behaviour of the BackgroundService class. All registered IHostedService will be started sequentially in the order they were registered. The application will not start until each IHostedService has returned from StartAsync. A BackgroundService is an IHostedService that starts your ExecuteAsync task before returning from StartAsync. Async methods will run until their first call to await an incomplete task before returning.
TLDR; If you don't await anything in your ExecuteAsync method, the server will never start.
Since you aren't awaiting that async method, your code boils down to;
while(true)
HandleClient(...);
(Do you really want to spawn an infinite number of TcpClient as fast as the CPU will go?). There's a really easy fix;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield();
// ...
}
working my way through all that is async / await (coming from threadpools) and I've hit an interesting challenge.
I have a TCP Server running in a WPF application that accepts clients and stores them in a List<> like such:
private List<Client> clients = new List<Client>();
while (running && clientCount <= maxClients)
{
Client client = new Client(await server.AcceptTcpClientAsync());
await client.WriteLineAsync("Connected to the Server!");
clients.Add(client);
clientCount++;
}
So what I want to do is iterate through the list of my Clients and if any data is received, I want to append it to a textbox. I realize this may not be the best way to achieve this, and I'm open to suggestions, but this is how I currently have it structured.
A button starts the loop and continuously calls and awaits AllReadLineAsync()
private async void btnStartReadLoopClick(object sender, RoutedEventArgs e)
{
btnStartReadLoop.IsEnabled = false;
while(server.clientCount > 0)
{
string text = await server.AllReadLineAsync();
txtOutputLog.AppendText("[client] " + text + "\n");
}
}
which is this function:
public async Task<string> AllReadLineAsync()
{
var tasklist = new List<Task<string>>();
foreach (var client in clients)
tasklist.Add(client.ReadLineAsync());
while (tasklist.Count > 0)
{
Task<string> finishedTask = await Task.WhenAny(tasklist);
if (finishedTask.Status == TaskStatus.RanToCompletion)
return await finishedTask;
tasklist.Remove(finishedTask);
}
return "Error: No task finished";
}
This function iterates through the list of clients and creates a List<Tast<string>> of all the ReadLineAsync() tasks.
At any given time, I may only have 1 or 2 clients actually sending data, so I can't WhenAll() and I've tried WhenAny() and WaitAny() without success.
Note to future googlers: WaitAny() is like Wait() and is blocking. Do not do this on a UI thread. Instead use WhenAny() and await it.
So my current implementation kind of works, but I can't figure out this bug where messages will build up from 1 client if the other clients don't send data.
TL;DR: Am I using WhenAny() correctly or is there a better way for me to await ReadLineAsync and pass the result to a textbox?
EDIT: Here's the behavior I'm seeing
I typed in this order: Left, Right, Left 2, Right 2, Left 3, Right 3
and it appears as if some messages are being dropped?
EDIT 2: I found the source of the code snippet I copied on the MSDN blog: https://blogs.msdn.microsoft.com/pfxteam/2012/08/02/processing-tasks-as-they-complete/
This code snippet is meant specifically to iterate through a list of tasks ensuring they all are completed. I don't care if tasks are duplicated though so I need to modify the code to always check the entire tasklist instead of removing any tasks.
it appears as if some messages are being dropped?
Yes. Because asynchronous work is started when you call their method (e.g., ReadLineAsync). When one of them completes (Task.WhenAny), your code abandons the other tasks. But they keep running - they're still reading from their sockets, and whatever they read is just going to be thrown away.
AFAIK, the behavior is undefined when you start reading from the same socket again - it's possible it may read what's next, or it's possible it may be queued. I do know that you're not supposed to do issue multiple reads from a socket (or any stream) simultaneously.
Sockets are not a perfect match to async, because they can send data at any time. You should use Rx or events instead. It's possible to make async work but it's extremely complex.
Alright so I figured out where I was going wrong and why my previous code didn't work.
First off, let's talk about what this code does, and why it doesn't work:
public async Task<string> AllReadLineAsync()
{
var tasklist = new List<Task<string>>();
foreach (var client in clients)
tasklist.Add(client.ReadLineAsync());
while (tasklist.Count > 0)
{
Task<string> finishedTask = await Task.WhenAny(tasklist);
if (finishedTask.Status == TaskStatus.RanToCompletion)
return await finishedTask;
tasklist.Remove(finishedTask);
}
return "Error: No task finished";
}
1) Creates a list of await ReadLineAsync()
2) While the size of that list is greater than 0, we wait for await any of the ReadLineAsync functions.
3) When we hit a Task that has finished, we return it's string, and exit the function
4) Any remaining ReadLineAsync functions that did not finish are still running, but we lost the reference to their instance.
5) This function loops, calling AllReadAsync() immediately after it finishes.
6) This causes us to try and access the StreamReady while it is still being awaited from step 4 - Thus throwing an excpetion.
Handling Multiple TcpClients with an Async TCP Server
Because of the structure of this, I could not come up with a way to use WhenAny() in my application. Instead I added this function to my Client Class:
public async Task<string> CheckForDataAsync()
{
if (!stream.DataAvailable)
{
await Task.Delay(5);
return "";
}
return await reader.ReadLineAsync();
}
Instead of awaiting ReadLineAsync(), we instead access the NetworkStream from the TcpClient, and we check if there is data available, if(!stream.DataAvailable), if there is not, we return early with an empty string, else we await ReadLineAsync() because we know we have incoming data, and we expect to receive the whole line.
We then replace the first function I talked about, AllReadLineAsync() With the following:
public async Task<string> AllReadLineAsync()
{
string data = "", packet = "";
foreach (var client in clients)
{
data = await client.CheckForDataAsync();
if (data != string.Empty)
packet += string.Format($"[client] {data}\n");
}
return packet;
}
Which is even simpler than the previous way I was trying. This now iterates through all of our clients in a for loop, and calls the CheckForDataAsync() function on each client. Since this functions returns early instead of infinitely awaiting a full ReadLineAsync() it does not continue to run in the background after the AllReadLineAysnc() function ends.
After we finish looping through all of our clients, we take our string packet and return it to the UI context, where we can then add our data to a text box as such:
private async void RecvData(object sender, RoutedEventArgs e)
{
while(server.hasClient)
{
string text = await server.AllReadLineAsync();
txtOutputLog.AppendText(text);
}
}
And that's it. That's how I'm handling multiple TcpClients from within a WPF application.
I am implementing a piece of software that reads a list of ids from a message queue. Once some come through, I would like to pass each one through a socket to a third party application, that will then process it and return a value back once it's done.
If the third party app takes too long to reply, I want to report this and maybe even close the connection.
Furthermore, this should run asynchronously, that is, once the messages are read from the queue, a separate task is started to handle it being sent to the socket and any subsequent communication.
Following this I have created a class that spawns a task and sends an exception after a timeout threshold.
public async Task Run(Action action, int timeoutInSeconds)
{
try
{
await Task.Run(action).TimeoutAfter(timeoutInSeconds);
}
catch (TimeoutException te)
{
//add error capture here or retry
}
}
public static async Task TimeoutAfter(this Task task, int timeoutInSeconds)
{
if (task == await Task.WhenAny(task, Task.Delay(timeoutInSeconds*1000)))
{
await task;
}
else
{
throw new TimeoutException(string.Format("Task {0} timed out after {1} seconds", task.Id, timeoutInSeconds));
}
}
Next I created another class to asynchronously listen to connections.
public class SocketListener
{
...
public async void Listen(Action action)
{
//initialization code
var listener = new TcpListener(ipAddress, Port);
listener.Start(numberOfConnections);
while (true)
{
try
{
//wait for client to connect
var client = await listener.AcceptTcpClientAsync();
//do something once client is connected
var task = new TaskWithTimeout();
await task.Run(() => action, 10);
}
catch (Exception ex)
{
// Log error
throw;
}
}
}
...
}
Here, after the client connects successfully, I want to call a method that will handle communication between server and client. If the client takes too long to respond, the TaskWithTimeout should throw an exception and move on.
My thought process was to call SocketListener once I read from the queue
public void ProcessQueue() {
//initialize SocketListener
listener.Listen(MethodToHandleCommunication)
...
}
Now I am a bit stuck. Preferably, SocketListener should be able to handle any type of communication, and that's why I thought I'd pass the Action as a parameter, so that I can determine what method I want to run from outside (by this I mean that if in the future I need to pass different data to the client, I would be able to reuse this code). However with this approach, I cannot even pass the client object back to the action.
In general I feel like I'm taking the wrong approach, and I am sure there's a better and more efficient way of doing what I want. As you can see I'm fairly new to parallel programming in general. I am a bit frustrated with this and would greatly appreciate any help or insight from SO
All solutions I found so far are based on WaitOne:
How to configure socket connect timeout
or spawning a worker thread
For me, blocking the thread with WaitOne defeats the purpose of async methods. Spawning another thread is not much better (as async model strives to use as less threads as possible).
Is there any other solution which would still let me abort the connection attempt without blocking the current thread or spawning another one?
I'm developing a library used by external code which has no knowledge of what's going on inside my library (the code just calls my ConnectAsync method and I do the rest including TcpClient.ConnectAsync and so on). The external code can be anything (web app, desktop app, Windows service, whatever). Ideally, the solution should not require the external code to do anything to abort the operation beyond setting .Timeout property of my class. However, if it's the only the way to avoid blocks or worker threads when implementing custom connection timeout, I'd be grateful to see which options I have (in terms of async/await model).
TcpClient.SendAsync doesn't receive a CancellationToken so it can't really be canceled (How do I cancel non-cancelable async operations?). You can use the WithTimeout extensions method:
public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult), TaskContinuationOptions.ExecuteSynchronously);
return Task.WhenAny(task, timeoutTask).Unwrap();
}
This doesn't cancel the original operation though, only allows your code to behave as if it did. The abandoned operation will linger forever unless handled explicitly.
To actually cancel the underlying operation you should make sure to call Dispose on the TcpClient (preferably via a using scope). That would make the abandoned task throw an ObjectDisposedException (or others) so be aware of that.
You can take a look at my answer here about using a TimeoutScope:
try
{
var client = new TcpClient();
using (client.CreateTimeoutScope(TimeSpan.FromSeconds(2)))
{
var result = await client.ConnectAsync();
// Handle result
}
}
catch (ObjectDisposedException)
{
return null;
}
If you create a second task for the timeout (Task.Delay does nicely), then you can use Task.WhenAny to complete as soon as either your task, or the timeout completes.
var timeout = Task.Delay(whatever);
var mightTimeout = Task.WhenAny(new {} { timeout, originalTask });
// let mightTimeout complete by whatever method (eg. async)
if (mightTimeout == timeout) {
// Timeout!!
// Put abort code in here.
}
I found a solution using
Await Task.WhenAny
Task.WhenAny will finish whenever any of the task included finishes first.
Put it inside an async function
Here's an example that works for me:
Public Async Function TCPConnectionAsync(HostIpAddress, Port) As Task(Of String)
Dim client As New TcpClient
Await Task.WhenAny(client.ConnectAsync(HostIpAddress, Porta), Task.Delay(3000))
'this above will not block because function is async,
'whenever the connection is successful or Task.Delay expires the task will end
' proceeding next, where you can check the connection
If client.Connected = False Then 'this will be evaluated after 3000ms
client.Close()
return "not connected"
else
'do here whatever you need with the client connection
client.Close()
return "all done"
End If
End Sub
For those who want to use asynchronous solution with async/await with timeout support:
public static async Task<bool> PingHostAndPort(string host, int port, int timeout)
{
using (var tcpClient = new TcpClient())
{
Task connectTask = tcpClient.ConnectAsync(host, port);
Task timeoutTask = Task.Delay(timeout);
// Double await is required to catch the exception.
Task completedTask = await Task.WhenAny(connectTask, timeoutTask);
try
{
await completedTask;
}
catch (Exception)
{
return false;
}
if (timeoutTask.IsCompleted)
{
return false;
}
return connectTask.Status == TaskStatus.RanToCompletion && tcpClient.Connected;
};
}
Note that you can use this logic (utilizing the timeoutTask) from any operations/methods that miss a timeout parameter.
I'm working on WCF client app, and I facing difficulties with the await/async pattern.
It seems that the line:
await client.LongOperationAsync();
always blocks. As I understand, the thread supposed to exit and continue on to the Main() method and then return when the async method completes, maybe I'm wrong.
The output for the code below is (always):
Test() started
Test() error
*
*
*
...
The Test() method always completes before the context returns to main. Any thoughts would be highly appreciated.
static void Main(string[] args)
{
Program p = new Program();
p.Test();
while (true)
{
Console.WriteLine("*");
Thread.Sleep(500);
}
}
private async Task Test()
{
Console.WriteLine("Test() started");
try
{
MySoapClient client = new MySoapClient(
new BasicHttpBinding(new BasicHttpSecurityMode()),
new EndpointAddress("http://badaddress"));
await client.LongOperationAsync();
Console.WriteLine("Test() success");
}
catch (Exception)
{
Console.WriteLine("Test() error");
return;
}
Console.WriteLine("Test() end successfully");
}
Async methods execute synchronously until the first await; if your LongOperationAsync method performs a blocking operation before its first await, the calling method will be blocked as well. I suspect that's what happening in your case.
This is probably because WebRequest.BeginGetResponse performs some of its work synchronously. See Stephen Toub's answer to this question:
The Async CTP's GetRequestStreamAsync and GetResponseAsync are simple
wrappers around the existing HttpWebRequest.BeginGetRequestStream and
BeginGetResponse in .NET 4. Those Begin* methods have a lot of setup
work they do (e.g. proxy, DNS, connection pooling, etc.) before they
can submit a request, and unfortunately today that work all happens
synchronously as part of the Begin* call.
In this case, you provided a bad domain name, so I suspect it takes a while for the DNS resolution to fail.