Raising events during Tasks causing Cross-Thread Exception - c#

I wrote a Client/Server async classes which works fine in the console. I created a WinForm project for the server which subscribes to an event thrown by the server when there is a .Pending() connection and writes some messages into a textbox - which causes a Cross-Thread exception. The exception does not surprise me, however I am looking for a way to invoke that event without causing this exception, without handling it on the GUI/Control with .InvokeRequired and .Invoke - if that is even possible?
The server is started like that:
Server server = new Server(PORT);
server.RunAsync();
in .RunAsync() i just iterate over the network devices and set them to listening and invoke an event that the server has started, this also writes into the GUI however without any issue.
public async Task RunAsync()
{
GetNetworkDevicesReady(Port);
await Task.Factory.StartNew(() =>
{
Parallel.ForEach(networkListeners, (listener) =>
{
Write.Info($"LISTENING ON {listener.LocalEndpoint}");
listener.Start();
});
});
IsRunning = true;
OnServerStarted?.Invoke(this, networkListeners.Where(l=>l.Active).ToList());
}
The code below is registered on the Form.Load event and does not cause a Cross-Thread exception when writing "SERVER STARTED" in the textbox.
server.OnServerStarted += (s, a) =>
{
consoleWindow1.Event("SERVER STARTED", $"{Environment.NewLine}\t{string.Join($"{Environment.NewLine}\t", a.Select(x=>x.LocalEndpoint))}");
consoleWindow1.Event("WAITING FOR PENDING CONNECTIONS");
server.WaitForConnectionsAsync();
};
And this is the code which runs indefinite until a cancellation token is triggered:
public async Task WaitForConnectionsAsync()
{
waitingForConnectionsToken = new CancellationTokenSource();
await (waitinfConnectionTaks=Task.Factory.StartNew(async () =>
{
while (!waitingForConnectionsToken.IsCancellationRequested)
{
foreach (var listener in networkListeners)
{
if (waitingForConnectionsToken.IsCancellationRequested) break;
if (!listener.Active)
{
continue;
}
if (listener.Pending())
{
try
{
TcpClient connection = await listener.AcceptTcpClientAsync();
//TODO: need to send it synchronised, since this causes a Cross-Thread when using WinForms
OnPendingConnection?.Invoke(this, connection);
}
catch (ObjectDisposedException x)
{
Write.Error(x.ToString());
}
}
}
}
}));
}
I know I can use the textbox .InvokeRequired and .Invoke on the GUI but I have the feeling that the server should throw the event in a way the GUI doesn't cause a Cross-Thread exception.
Is there a way to invoke the eventhandler in that "infinite task" without causing this exception?

Thanks to the comments and a good portion of sleep I solved my issue by changing the WaitForConnectionsAsync to the following code:
List<TcpClient> connections = new List<TcpClient>();
public async Task WaitForConnectionsAsync()
{
await (waitinfConnectionTaks = Task.Factory.StartNew(async () =>
{
//REMOVED LOOP
// while (!waitingForConnectionsToken.IsCancellationRequested)
{
foreach (var listener in networkListeners)
{
if (waitingForConnectionsToken.IsCancellationRequested) break;
if (!listener.Active)
{
continue;
}
if (listener.Pending())
{
try
{
TcpClient connection = await listener.AcceptTcpClientAsync();
//RETAIN CONNECTIONS IN A LIST
connections.Add(connection);
}
catch (ObjectDisposedException x)
{
Write.Error(x.ToString());
}
}
}
}
}));
//ITERATE OVER CONNECTIONS
foreach (var connection in connections)
{
//INVOKE EVENT
OnPendingConnection?.Invoke(this, connection);
}
//CLEAR THE LIST
connections.Clear();
//RESTART THE TASK
if(!waitingForConnectionsToken.IsCancellationRequested)
WaitForConnectionsAsync();
}
So Basically i am catching all pending connections into a list, once the work has completed, I run over the list, fire the event with each connection, clear the list and then start the task again. This code change doesn't throw the Cross-Thread exception anymore.
An improvement i could add now is to accept a collection in the event instead a single connection.
If you have any improvements or better practice suggestions, please let me know.

Related

client.GetStreamAsync(url) is freezing my UI

So I'm listening to an Server side event with my code to just write it on the console (for now) but it seems that this is making my window's form UI freeze
The code in question (which I'm calling from the main form's function)
static async Task hello()
{
HttpClient client = new HttpClient();
//client.Timeout = TimeSpan.FromSeconds(5);
while (true)
{
try
{
Console.WriteLine("Establishing connection");
using (var streamReader = new StreamReader(await client.GetStreamAsync(url)))
{
while (!streamReader.EndOfStream)
{
var message = await streamReader.ReadLineAsync();
Console.WriteLine(message.ToString());
}
}
}
catch (Exception ex)
{
//Here you can check for
//specific types of errors before continuing
//Since this is a simple example, i'm always going to retry
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Retrying in 5 seconds");
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}
Thanks in advance
I've solved the problem, it appears that async/await task freezes the GUI. To stop this from happening you need to use Task.Run(() => your_function()); when you call an async function
This question might be a possible duplicate of: GUI freezes when using async/await ... so go there if you want to find a bit more knowledge about the subject

How to correctly finish RabbitMq consumer and wait to RabbitMq consumer threads (library consumer callbacks) after close connection and model?

I have RabbitMq consumer (RabbitMQ.Client.Events.EventingBasicConsumer) that process incoming messages.
But I noticed that if close connection and model they does not wait to finish library processing threads. E.g.:
If I will add thread sleep for several seconds to EventingBasicConsumer::Received callback then I noticed that Close functions (Close() of IModel and IConnection) finished before exit from this consumer callback
After finish Close functions I continue to receive some messages to EventingBasicConsumer::Received callback.
So how to correctly close consumer and wait to finish all processing's in consumer threads of library?
I want to ensure that I will not receive any incoming messages from library for my consumer after close all connections/consumers.
Simplified code:
RunTest()
{
MyConsumer consumer = new MyConsumer();
consumer.Connect();
// Wait before close for process some count of incoming messages
Thread.Sleep(10 * 1000);
consumer.Disconnect();
}
class MyConsumer
{
private RabbitMQ.Client.IConnection m_Connection = null;
private RabbitMQ.Client.IModel m_Channel = null;
public void Connect()
{
//
// ...
//
m_Channel = m_Connection.CreateModel();
m_Consumer = new RabbitMQ.Client.Events.EventingBasicConsumer(m_Channel);
m_Consumer.Received += OnRequestReceived;
m_ConsumerTag = m_Channel.BasicConsume(m_Config.RequestQueue, false, m_Consumer);
}
public void Disconnect()
{
Console.WriteLine("---> IModel::Close()");
m_Channel.Close();
Console.WriteLine("<--- IModel::Close()");
Console.WriteLine("---> RabbitMQ.Client.IConnection::Close()");
m_Connection.Close();
Console.WriteLine("<--- RabbitMQ.Client.IConnection::Close()");
//
// Maybe there is need to do some RabbitMQ API call of channel/model
// for wait to finish of all consumer callbacks?
//
m_Channel = null;
m_Connection = null;
}
private void OnRequestReceived(object sender, RabbitMQ.Client.Events.BasicDeliverEventArgs mqMessage)
{
Console.WriteLine("---> MyConsumer::OnReceived");
Console.WriteLine("MyConsumer: ThreadSleep started");
Thread.Sleep(10000);
Console.WriteLine("MyConsumer: ThreadSleep finished");
if (m_Channel != null)
{
m_Channel.BasicAck(mqMessage.DeliveryTag, false);
}
else
{
Console.WriteLine("MyConsumer: already closed");
}
Console.WriteLine("<--- MyConsumer::OnReceived");
}
}
Result:
---> MyConsumer::OnReceived
MyConsumer: ThreadSleep started
---> IModel::Close()
<--- IModel::Close()
---> RabbitMQ.Client.IConnection::Close()
<--- RabbitMQ.Client.IConnection::Close()
MyConsumer: ThreadSleep finished
MyConsumer: already closed
<--- MyConsumer::OnReceived
---> MyConsumer::OnReceived
MyConsumer: ThreadSleep started
MyConsumer: ThreadSleep finished
MyConsumer: already closed
<--- MyConsumer::OnReceived
How we see MyConsumer::OnReceived was finished after exit from Close() functions of Consumer and Connection. Moreover how we see there is one more message which was income after finish of previous call of OnReceived and close connection (that means that RqbbitMq continues to process consumer messages until the internal library queues are empty ignoring the fact that consumer and connection are already closed).
This is really bug in RabbitMQ.Client (v5.1.2). Source code of ConsumerWorkService.cs:
namespace RabbitMQ.Client
{
public class ConsumerWorkService
{
...
class WorkPool
{
readonly ConcurrentQueue<Action> actions;
readonly AutoResetEvent messageArrived;
readonly TimeSpan waitTime;
readonly CancellationTokenSource tokenSource;
readonly string name;
public WorkPool(IModel model)
{
name = model.ToString();
actions = new ConcurrentQueue<Action>();
messageArrived = new AutoResetEvent(false);
waitTime = TimeSpan.FromMilliseconds(100);
tokenSource = new CancellationTokenSource();
}
public void Start()
{
#if NETFX_CORE
System.Threading.Tasks.Task.Factory.StartNew(Loop, System.Threading.Tasks.TaskCreationOptions.LongRunning);
#else
var thread = new Thread(Loop)
{
Name = "WorkPool-" + name,
IsBackground = true
};
thread.Start();
#endif
}
public void Enqueue(Action action)
{
actions.Enqueue(action);
messageArrived.Set();
}
void Loop()
{
while (tokenSource.IsCancellationRequested == false)
{
Action action;
while (actions.TryDequeue(out action))
{
try
{
action();
}
catch (Exception)
{
}
}
messageArrived.WaitOne(waitTime);
}
}
public void Stop()
{
tokenSource.Cancel();
}
}
}
}
As we see there is no any waitings of thread var thread = new Thread(Loop). So really event RabbitMQ.Client.Events.EventingBasicConsumer::Received can be fired anytime even when there is no consumer or connection for a long time which closed long time ago till internal library queue will empty. As I supposed(( :
Action action;
while (actions.TryDequeue(out action))
{
try
{
action();
}
catch (Exception)
{
}
}
So IModel::Close() will set only CancelationToken without join of thread and there is need some workaround for this Bug.
Just to confirm #Alexander's findings, this issue is still present in v6.2.2 of the .Net Client and the issue is also present with the event driven callback consumer.
I've found that:
The 'Received' callback registered with both the EventingBasicConsumer and AsyncEventingBasicConsumer will be invoked for up to the prefetchCount setting of the BasicQos setup on the channel, after the connection is closed.
In manual Ack mode, the call to BasicAck will throw a RabbitMQ.Client.Exceptions.AlreadyClosedException if the connection is closed, and the message won't be acked from the queue, however, the callback will continue to process subsequent messages. This could lead to idempotence problems with receiving the same message more than once during a disconnect.
This may be another good reason to ensure that the prefetchCount is set to a finite and sane value (in addition to e.g. Memory considerations of unbounded prefetch counts).
Finally, if I deliberately close the Connection unexpectedly (e.g. during testing), I found that I needed to explicitly detach my consumer Received handler AND explicitly call consumerChannel.Close(); (i.e. IModel.Close) before I could create a new connection (the Rabbit client would tend to 'hang').
The connection.ConnectionShutdown event doesn't seem to fire reliably if I have set a ConsumerDispatchConcurrency > 1 with the synchronous EventingBasicConsumer
The Shutdown event on AsyncEventingBasicConsumer doesn't fire either, if I have ConsumerDispatchConcurrency > 1` when using the async consumer.
However, I did find that the ModelShutdown event on the IModel / channel fires reliably for sync / async and concurrency > 1.
consumerChannel.ModelShutdown += (sender, args) =>
{
consumer.Received -= handler; // e.g. AsyncEventingBasicConsumer
consumerChannel.Close(); // IModel
};

Task Chaining when every task is dependent on certain events

I have a requirement to connect to the server and collect data for processing.
Here is the Connect method
private readonly ServerComLibrary _vMyServer;// this is initialised in constructor
public ConnectToServer(string servername)
{
_vMyServer.connectToServerByName("ssl",servername);
}
_vMyServer has below events
onConnectSucceeded - I will collect data and copy it to excel file
onConnectFailed - just log the exception
Here is a connection success event
private void Handle_OnConnectSucceeded()
{
//collect data and create excel
}
Here is a Failed event
private void Handle_OnConnectFailed(int hr)
{
//log exception
}
Everything works fine!
But, now my requirement is to connect to multiple servers one by one
List<Server> servers = ConfigurationManager.GetSection("servers") as List<Server>;
var datacollectionTasks = new List<Task>();
foreach (var server in servers)
{
var data = Task.Run(() => ConnectToServer(server.serveraddress));
datacollectionTasks.Add(dataFix);
}
await Task.WhenAll(datacollectionTasks);
I want to start the second task only after first task of connecting to the server and creating excel generation compleats or connection fails.
How can I do it? I may use ContinueWith but not sure how to confirm if events fired and job completed.
my requirement is to connect to multiple servers one by one
Then I'm not sure why you're using Task.WhenAll.
How can I do it? I may use ContinueWith but not sure how to confirm if events fired and job completed.
In order to chain tasks, you need a task to chain onto. Specifically, change the events into a Task by using TaskCompletionSource<T>:
public static class ServerComLibraryExtensions
{
public static Task ConnectAsync(this ServerComLibrary #this, string protocol, string host)
{
var tcs = new TaskCompletionSource<object>();
Action onSucceeded = null;
Action<int> onFailed = null;
onSuccess = () =>
{
#this.OnConnectSucceeded -= onSucceeded;
#this.OnConnectFailed -= onFailed;
tcs.TrySetResult(null);
};
onFailed = hr =>
{
#this.OnConnectSucceeded -= onSucceeded;
#this.OnConnectFailed -= onFailed;
tcs.TrySetException(Marshal.GetExceptionForHR(hr));
};
#this.OnConnectSucceeded += onSucceeded;
#this.OnConnectFailed += onFailed;
#this.connectToServerByName(protocol, host);
return tcs.Task;
}
}
Now that the connect operation is represented as a Task instead of events, it can naturally be "chained" by using the await keyword:
List<Server> servers = ConfigurationManager.GetSection("servers") as List<Server>;
foreach (var server in servers)
{
await _vMyServer.ConnectAsync("ssl", server.serveraddress);
// collect data and create excel
}

C# - asynchronous sockets with timeout

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

App freezes after Dispatcher.Invoke

I have this application that freezes when calling the dispatcher.invoke for any control.
When i Call the Dispatcher in radiobutton, Grid, Image..etc the App freezes but without giving errors. any help please !!! thank you
I call the thread Method RunClient
private void RunClient()
{
TcpClient client;
// instantiate TcpClient for sending data to server
try
{
// Step 1: create TcpClient and connect to server
client = new TcpClient();
client.Connect(ip, 5001);
// Step 2: get NetworkStream associated with TcpClient
output = client.GetStream();
// create objects for writing and reading across stream
writer = new BinaryWriter(output);
reader = new BinaryReader(output);
string theReply = "";
do
{
try
{
// read the string sent to the server
theReply = reader.ReadString();
int i = 0;
foreach (var x in theReply.Split('#'))
{
ReadString[i] = x;
i++;
}
CheckConnection(ReadString[0]);
}
catch (Exception)
{
//do nothing
}
} while (ReadString[6].Equals(" ") &&
connection.Connected);
updatelabel = () => GameResult(ReadString[6]);
Dispatcher.Invoke(new Action(updatelabel));
if (!connection.Connected)
{
MessageBox.Show("The connection was lost. The game will be closed automatically.");
writer.Close();
reader.Close();
output.Close();
connection.Close();
this.Close();
}
}
// handle exception if error in establishing connection
catch (Exception error)
{
MessageBox.Show("Check Internet Connectivity. Couldn't connect!");
}
}
when the code enters the method ( check connection ) and calls the dispatcher the app freezes.
void CheckConnection(string ii)
{
try
{
if (ii.Equals("Connected"))
{
MessageBox.Show("A Connection was established");
int x = Convert.ToInt32(ReadString[1]);
if (x == 1)
{
updatelabel = () => char1RadioButton2.IsEnabled = false;
char1RadioButton2.Dispatcher.Invoke(new Action(updatelabel));
}
else
{
updatelabel = () => char5RadioButton2.IsEnabled = false;
char5RadioButton2.Dispatcher.Invoke(new Action(updatelabel));
}
updatelabel = () => CreatingGameGrid.Visibility = System.Windows.Visibility.Visible;
CreatingGameGrid.Dispatcher.Invoke(new Action(updatelabel));
updatelabel = () => JoinGameGrid.Visibility = System.Windows.Visibility.Visible;
JoinGameGrid.Dispatcher.Invoke(new Action(updatelabel));
}
else
{
MessageBox.Show("No Such Game found");
this.Close();
}
}
catch (Exception x)
{
MessageBox.Show(x.ToString());
}
}
The Dispatcher.Invoke attempts to synchronously run the specified action on the Dispatcher Thread.
Assuming the RunClient is run on the Dispatcher Thread, and the while loop continues to run while you are trying to Invoke back onto the Dispatcher Thread, the call will freeze.
The simplest solution is to replace all the Dispatcher.Invoke with Dispatcher.BeginInvoke and give it a priority that will run once your RunClient is finished.
The other solution is to run RunClient on a BackgroundWorker.
Similar questions with answers are
Dispatcher.Invoke loop freeze UI
Dispatcher.Invoke hangs main window.
Response to comment on ReadString freeze
Calling Read on a NetworkStream is a blocking call. Well, in fact, it is the Stream obtained by calling TcpClient.GetStream() that blocks. The documentation on MSDN states 'After you have obtained the NetworkStream, call the Write method to send data to the remote host. Call the Read method to receive data arriving from the remote host. Both of these methods block until the specified operation is performed'.
I used dotPeek to see what ReadString was doing and the first thing it does is read the length of the incoming string off the stream using NetworkStream.ReadByte which will block until it has a value to read.
That means the ReadString will sit there until there is data available to read and the amount of data is the same as or more than is expected. You will need to check if you have anything to read before you do by calling stream.DataAvailable or reader.PeekChar.
Alternatively, you could run your socket code on a separate thread. If you are using .Net 4.5, I would take a good look at the Task Parallel Library. ntziolis says in an answer to this question that 'We have made good experiences with that (long being days rather than minutes or hours).'

Categories