I just can't seem to understand how to structure an asynchronous call to SendPingAsync. I want to loop through a list of IP addresses and ping them all asynchronously before moving on in the program... right now it takes forever to go through all of them one at a time. I asked a question about it earlier thinking I'd be able to figure out async but apparently I was wrong.
private void button1_Click(object sender, EventArgs e)
{
this.PingLoop();
MessageBox.Show("hi"); //for testing
}
public async void PingLoop()
{
Task<int> longRunningTask = PingAsync();
int result = await longRunningTask;
MessageBox.Show("async call is finished!");
//eventually want to loop here but for now just want to understand how this works
}
private async Task<int> PingAsync()
{
Ping pingSender = new Ping();
string reply = pingSender.SendPingAsync("www.google.com", 2000).ToString();
pingReplies.Add(reply); //what should i be awaiting here??
return 1;
}
I'm afraid I just don't get what is really going on here enough... when should I return a task? When I run this as is I just get a frozen UI and a ping error. I have read the MSDN documentation and tons of questions here and I'm just not getting it.
You'd want to do something like:
private async Task<List<PingReply>> PingAsync()
{
var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
This will start off one request per IP in theListOfIPs asynchronously, then asynchronously wait for them all to complete. It will then return the list of replies.
Note that it's almost always better to return the results vs. setting them in a field, as well. The latter can lead to bugs if you go to use the field (pingReplies) before the asynchronous operation completes - by returning, and adding the range to your collection after the call is made with await, you make the code more clear and less bug prone.
What you do here pingSender.SendPingAsync("www.google.com", 2000).ToString(); doesn't make much sense.
Instead you should return pingSender.SendPingAsync("www.google.com", 2000) and
await Task.WhenAll(your all ping requests)
What you want is to start all pings at once:
var pingTargetHosts = ...; //fill this in
var pingTasks = pingTargetHosts.Select(
host => new Ping().SendPingAsync(host, 2000)).ToList();
Now the pings are running. Collect their results:
var pingResults = await Task.WhenAll(pingTasks);
Now the concurrent phase of the processing is done and you can examine and process the results.
Here is how I do it
private delegate void scanTargetDelegate(IPAddress ipaddress);
private Task<PingReply> pingAsync(IPAddress ipaddress)
{
var tcs = new TaskCompletionSource<PingReply>();
try
{
AutoResetEvent are = new AutoResetEvent(false);
Ping ping = new Ping();
ping.PingCompleted += (obj, sender) =>
{
tcs.SetResult(sender.Reply);
};
ping.SendAsync(ipaddress, new object { });
}
catch (Exception)
{
}
return tcs.Task;
}
in a BackgroundWorker I do this
List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
addStatus("Scanning Network");
foreach (var ip in range)
{
pingTasks.Add(pingAsync(ip));
}
Task.WaitAll(pingTasks.ToArray());
addStatus("Network Scan Complete");
scanTargetDelegate d = null;
IAsyncResult r = null;
foreach (var pingTask in pingTasks)
{
if (pingTask.Result.Status.Equals(IPStatus.Success))
{
d = new scanTargetDelegate(scanTarget); //do something with the ip
r = d.BeginInvoke(pingTask.Result.Address, null, null);
Interlocked.Increment(ref Global.queuedThreads);
}
else
{
if (!ownIPs.Contains(pingTask.Result.Address))
{
failed.Add(pingTask.Result.Address);
}
}
}
if (r != null)
{
WaitHandle[] waits = new WaitHandle[] { r.AsyncWaitHandle };
WaitHandle.WaitAll(waits);
}
public static async Task<bool> PingAsync(string host)
{
try
{
var ping = new System.Net.NetworkInformation.Ping();
var reply = await ping.SendTaskAsync(host);
return (reply.Status == System.Net.NetworkInformation.IPStatus.Success);
}
catch { return false; }
}
Related
Good day.
I'm having a problem exiting a task with the cancellation token.
My program freezes when I get to the token2.ThrowIfCancellationRequested();.
Following it with the breakpoints is shows that the token2 is cancelled, but the program doesn't revert back to the previous sub routine where I try and catch
try
{
Task.Run(() => SendData_DoWork(_tokenSource3));
}
catch (OperationCanceledException ex)
{
SetText("Communivation error with device");
SetText("");
}
finally
{
token.Dispose();
}
}//comms routine
//send Meter Address to communicate to meter
private void SendData_DoWork(CancellationTokenSource token)
{
var token2 = token.Token;
var _tokenSource4 = new CancellationTokenSource();
try
{
timer.Interval = 10000;
timer.Start();
timer.Elapsed += OnTimerElapsed;
NetworkStream stream = client.GetStream();
SerialConverter serialConverter = new SerialConverter();
Thread.Sleep(1000);
string newtext = null;
newtext = $"/?{address}!\r\n";
SetText("TX: " + newtext);
byte[] newData = stringSend(newtext);
stream.Write(newData, 0, newData.Length);
Thread.Sleep(50);
byte[] message = new byte[23];
int byteRead;
while (true)
{
byteRead = 0;
try
{
byteRead = stream.Read(message, 0, 23);
if (message[0] == (char)0x15)
{
token.Cancel();
}
}
catch
{
token.Cancel();
}
if ((byteRead == 0))
{
token.Cancel();
}
timer.Stop();
timer.Dispose();
ASCIIEncoding encoder = new ASCIIEncoding();
string newresponse = encoder.GetString(serialConverter.convertFromSerial(message));
SetText("RX: " + newresponse);
if (newresponse[0].ToString() == SOH)
{
token.Cancel();
}
if (newresponse != null)
{
/* NEXT SUB ROUTINE*/
}
else { break; }
}//while looop
}//try
catch (Exception ex)
{
token.Cancel();
}
if (token2.IsCancellationRequested)
{
timer.Stop();
timer.Dispose();
token2.ThrowIfCancellationRequested();
}
}//sendData subroutine
You are launching a Task, and ignoring the result; the only time Task.Run would throw is if the task-method is invalid, or enqueuing the operation itself failed. If you want to know how SendData_DoWork ended, you'll need to actually check the result of the task, by capturing the result of Task.Run and awaiting it (preferably asynchronously, although if we're talking async, SendData_DoWork should probably also be async and return a Task).
Your catch/finally will probably be exited long before SendData_DoWork even starts - again: Task.Run just takes the time required to validate and enqueue the operation; not wait for it to happen.
I think you have missunderstood how cancellation tokens are supposed to work. Your work method should take a CancellationToken, not a CancellationTokenSource. And it should call ThrowIfCancellationRequested inside the loop, not after. I would suspect that you would get some issues with multiple cancel calls to the same cancellation token.
Typically you would use a pattern something like like this:
public void MyCancelButtonHandler(...) => cts.Cancel();
public async void MyButtonHandler(...){
try{
cts = new CancellationTokenSource(); // update shared field
await Task.Run(() => MyBackgroundWork(cts.Token));
}
catch(OperationCancelledException){} // Ignore
catch(Exception){} // handle other exceptions
}
private void MyBackgroundWork(CancellationToken cancel){
while(...){
cancel.ThrowIfCancellationRequested();
// Do actual work
}
}
So in my particular case it seems like changing the sub-routines from private async void ... to private async Task fixes the particular issue that I'm having.
I've an existing code I wrote some time ago, that works but I dislike the fact that the thread I start remains in loop.
This piece of code is a consumer on an IBMMQ code, waiting for messages to be processed.The problem I've is that with the following code
private Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
ConnectToAccessQueue();
Logger.Debug($"Accessed to the queue {queueName}");
Logger.DebugFormat("Repeating timer started, checking frequency: {checkingFrequency}",
checkingFrequency);
while (!cancellationToken.IsCancellationRequested)
{
Logger.Trace( () => "Listening on queues for new messages");
// isChecking = true;
var mqMsg = new MQMessage();
var mqGetMsgOpts = new MQGetMessageOptions
{ WaitInterval = (int)checkingFrequency.TotalMilliseconds };
// 15 second limit for waiting
mqGetMsgOpts.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING |
MQC.MQCNO_RECONNECT_Q_MGR | MQC.MQOO_INPUT_AS_Q_DEF;
try
{
mqQueue.Get(mqMsg, mqGetMsgOpts);
if (string.Compare(mqMsg.Format, MQC.MQFMT_STRING, StringComparison.Ordinal) == 0)
{
var text = mqMsg.ReadString(mqMsg.MessageLength);
Logger.Debug($"Message received : [{text}]");
Message message = new Message { Content = text };
foreach (var observer in observers)
observer.OnNext(message);
}
else
{
Logger.Warn("Non-text message");
}
}
catch (MQException ex)
{
if (ex.Message == MQC.MQRC_NO_MSG_AVAILABLE.ToString())
{
Logger.Trace("No messages available");
//nothing to do, emtpy queue
}
else if (ex.Message == MQC.MQRC_CONNECTION_BROKEN.ToString())
{
Logger.ErrorException("MQ Exception, trying to recconect", ex);
throw new ReconnectException();
}
}
Thread.Sleep(100);
}
},cancellationToken);
}
//Calling method
try
{
string queueManagerName = configuration.GetValue<string>("IBMMQ:QUEUE_MANAGER_NAME");
// var queueManager = new MQQueueManager(queueManagerName,dictionary2);
QueueMonitor monitor = new QueueMonitor(configuration, "IMPORTER_RECEIVER_TEST");
//_subscription = monitor.Subscribe(receiver);
await monitor.StartAsync(cts.Token).ConfigureAwait(false);
}
catch (Exception e)
{
log.Error(e, "Error creating the queue monitor or it's subscription");
}
finally
{
WaitForCancel(cts);
}
The call to await monitor.StartAsync(cts.Token).ConfigureAwait(false); remains pending.
How should I modify my code, so that the call returns and in background the task continue to loop?
Thanks in advance
Here is how you can simplify your code by replacing Thread.Sleep with Task.Delay:
private async Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
while (true)
{
// Process mqQueue here
await Task.Delay(100, cancellationToken);
}
}
Task.Delay has the advantage that accepts a CancellationToken, so in case of cancellation the loop will exit immediately. This could be important if the pooling of the MQ was lazier (for example every 5 seconds).
private static Task _runningTask;
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
_runningTask = ExecuteQueuePolling(cts.Token);
WaitForCancel(cts);
}
private static void WaitForCancel(CancellationTokenSource cts)
{
var spinner = new SpinWait();
while (!cts.IsCancellationRequested
&& _runningTask.Status == TaskStatus.Running) spinner.SpinOnce();
}
private static Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
var t = new Task(() =>
{
while (!cancellationToken.IsCancellationRequested)
; // your code
if (cancellationToken.IsCancellationRequested)
throw new OperationCanceledException();
}, cancellationToken, TaskCreationOptions.LongRunning);
t.Start();
return t;
}
I spent a couple of days searching in Google and trying to understand why in my case Windows Forms UI is blocked when executing pings in Tasks.
I saw a lot of similar cases, but none of them explains my specific case.
Issue description:
I have an application which sends pings asynchronously. Each ping is send inside of a Task. I use .ContinueWith to receive result of a ping and print it to textbox without blocking UI thread. It works OK if I launch all pings once. If I add a while {run} loop to make them run forever my UI becomes unresponsive and blocked, and none of the results are printed to the textbox.
Problematic Code:
Action action2 = () => {
for (int i = 0; i < ipquantity; i++)
{
int temp1 = i;
string ip = listView1.Items[temp1].SubItems[1].Text;
if (finished[temp1] == true) // Variable helps to check if ping reply was received and printed
continutask[temp1] = Task<string>.Run(() => PingStart(ip, temp1)).ContinueWith(antecedent => PrintResult(antecedent.Result, temp1));
}
};
while (run)
{
action2();
Thread.Sleep(1000);
}
Questions:
Why is the UI blocked with a while loop and why is it not blocked without it?
How can I modify my code to be still able to use Tasks for pings without blocking the UI?
Is there a better way to launch endless pings to several IP addresses simultaneously?
Complete code:
private async void buttonStart_Click(object sender, EventArgs e)
{
run = true;
int count = listView1.Items.Count;
task = new Task<string>[count];
result1 = new string[count];
finished = new bool[count];
continutask = new Task[count];
for (int i = 0; i < count; i++)
{
finished[i] = true;
}
Action action2 = () =>
{
for (int i = 0; i < count; i++)
{
int temp1 = i;
string ip = listView1.Items[temp1].SubItems[1].Text;
if (finished[temp1] == true)
continutask[temp1] = Task<string>.Run(() => PingStart(ip, temp1)).ContinueWith(antecedent => PrintResult(antecedent.Result, temp1));
}
};
while (run)
{
action2();
//await Task.Delay;
//Thread.Sleep(1000);
}
}
public void PrintResult(string message, int seqnum)
{
Action action = () =>
{
textBox1.AppendText(message);
textBox1.AppendText(Environment.NewLine);
textBox1.AppendText("");
textBox1.AppendText(Environment.NewLine);
};
if (InvokeRequired)
Invoke(action);
else
action();
finished[seqnum] = true;
}
public string PingStart(string ip, int seqnum)
{
finished[seqnum] = false;
Ping isPing = new Ping();
PingReply reply;
const int timeout = 2000;
const string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
var buffer = Encoding.ASCII.GetBytes(data);
PingOptions options = new PingOptions();
// Use the default Ttl value which is 128,
options.DontFragment = false;
reply = isPing.Send(ip, timeout, buffer, options);
string rtt = (reply.RoundtripTime.ToString());
string success = "N/A";
if (reply.Status == IPStatus.Success)
{
success = $"{ip}" + " Success!" + $" rtt: [{rtt}]" + $"Thread: {Thread.CurrentThread.GetHashCode()} Is pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
}
else if (reply.Status != IPStatus.Success)
{
success = $"{ip}" + $" Not Successful! Status: {reply.Status}" + $"Thread: {Thread.CurrentThread.GetHashCode()} Is pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
}
return success;
}
Since you already create (and save) your tasks, the easiest fix would be to await them for each iteration of your while loop:
while (run)
{
action2();
foreach (Task t in continutask)
await t;
}
That way, when all pings completed (successful or not) you start the entire process again - without delay.
One more thing: You could add a textBox1.ScrollToEnd(); to PrintResult
Since there is a lot of room for improvement, below is a rewritten and simplified example. I've removed a lot of unused variables (e.g. seqnum) and made the PingStart method completely asynchronous. I also replaced your ListBox with a TextBox for easier testing, so you might want to revert that in your code.
This still isn't the cleanest of all possible implementations (mainly because of the global run) but it should show you how to do things "more async" :)
private async void buttonStart_Click(object sender, EventArgs e)
{
// If the ping loops are already running, don't start them again
if (run)
return;
run = true;
// Get all IPs (in my case from a TextBox instead of a ListBox
string[] ips = txtIPs.Text.Split(new[] {"\r\n"}, StringSplitOptions.RemoveEmptyEntries);
// Create an array to store all Tasks
Task[] pingTasks = new Task[ips.Length];
// Loop through all IPs
for(int i = 0; i < ips.Length; i++)
{
string ip = ips[i];
// Launch and store a task for each IP
pingTasks[i] = Task.Run(async () =>
{
// while run is true, ping over and over again
while (run)
{
// Ping IP and wait for result (instead of storing it an a global array)
var result = await PingStart(ip);
// Print the result (here I removed seqnum)
PrintResult(result.Item2);
// This line is optional.
// If you want to blast pings without delay,
// you can remove it
await Task.Delay(1000);
}
}
);
}
// Wait for all loops to end after setting run = false.
// You could add a mechanism to call isPing.SendAsyncCancel() instead of waiting after setting run = false
foreach (Task pingTask in pingTasks)
await pingTask;
}
// (very) simplified explanation of changes:
// async = this method is async (and therefore awaitable)
// Task<> = This async method returns a result of type ...
// Tuple<bool, string> = A generic combination of a bool and a string
// (-)int seqnum = wasn't used so I removed it
private async Task<Tuple<bool, string>> PingStart(string ip)
{
Ping isPing = new Ping();
const int timeout = 2000;
const string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
var buffer = Encoding.ASCII.GetBytes(data);
PingOptions options = new PingOptions {DontFragment = false};
// await SendPingAsync = Ping and wait without blocking
PingReply reply = await isPing.SendPingAsync(ip, timeout, buffer, options);
string rtt = reply.RoundtripTime.ToString();
bool success = reply.Status == IPStatus.Success;
string text;
if (success)
{
text = $"{ip}" + " Success!" + $" rtt: [{rtt}]" + $"Thread: {Thread.CurrentThread.GetHashCode()} Is pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
}
else
{
text = $"{ip}" + $" Not Successful! Status: {reply.Status}" + $"Thread: {Thread.CurrentThread.GetHashCode()} Is pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
}
// return if the ping was successful and the status message
return new Tuple<bool, string>(success, text);
}
This way you will have a loop for each IP that will continue independently of each other until run is set to false.
Thread.Sleep(n) blocks the current thread for n milliseconds. If I understand the code correctly it executes action2 then suspends the calling thread for one second. If that thread is the main (UI) thread, your UI will be blocked.
Maybe moving the while loop to yet another thread would fix the problem.
I am trying to use inter process communication to send from one instance of my program its content to the other instance. The code I have under here is working but it makes the first instance freeze and only continues running when the second instansce is created and the data is passed back.I suspect its because messaging_server0() is not a async task. How would one approach this problem. Is there a way to make messaging_server0 async? or am I missing something?
main loop contains this piece of code
var makeTask = Task<string>.Factory.StartNew(() => pipe_server.messaging_server0());
if (makeTask.Result != null) {
dataGrid_logic.DataGridLoadTarget(makeTask.Result);
}
and on the other side I have messaging_server0
public static string messaging_server0()
{
using (var messagebus1 = new TinyMessageBus("ExampleChannel"))
{
string ret = null;
messagebus1.MessageReceived += (sender, e) => ret = Encoding.UTF8.GetString(e.Message);
while (true)
{
if (ret != null)
{
return ret;
}
}
}
}
The method names are going to be refactored.
I would suggest following approach:
public static async Task<string> messaging_server0()
{
using (var messagebus1 = new TinyMessageBus("ExampleChannel"))
{
var taskCompletition = new TaskCompletionSource<string>();
messagebus1.MessageReceived +=
(sender, e) =>
{
var ret = Encoding.UTF8.GetString(e.Message);
taskCompletition.TrySetResult(ret);
};
return await taskCompletition.Task;
}
}
Obviously you would need to add some error handling, time outs if needed etc.
with the help of alek kowalczyk and some digging I came up with this code
private async void xxx()
{
var makeTask = Task<string>.Factory.StartNew(() => pipe_server.messaging_server());
await pipe_server.messaging_server();
{
dataGrid_logic.DataGridLoadTarget(makeTask.Result);
}
}
plus the snippet he posted.
This works perfectly. thanks
This might be a duplicate of this question but that's confused with talk about batching database updates and still has no proper answer.
In a simple example using Azure Service Bus queues, I can't access a BrokeredMessage after it's been placed on a queue; it's always disposed if I read the queue from another thread.
Sample code:
class Program {
private static string _serviceBusConnectionString = "XXX";
private static BlockingCollection<BrokeredMessage> _incomingMessages = new BlockingCollection<BrokeredMessage>();
private static CancellationTokenSource _cancelToken = new CancellationTokenSource();
private static QueueClient _client;
static void Main(string[] args) {
// Set up a few listeners on different threads
Task.Run(async () => {
while (!_cancelToken.IsCancellationRequested) {
var msg = _incomingMessages.Take(_cancelToken.Token);
if (msg != null) {
try {
await msg.CompleteAsync();
Console.WriteLine($"Completed Message Id: {msg.MessageId}");
} catch (ObjectDisposedException) {
Console.WriteLine("Message was disposed!?");
}
}
}
});
// Now set up our service bus reader
_client = GetQueueClient("test");
_client.OnMessageAsync(async (message) => {
await Task.Run(() => _incomingMessages.Add(message));
},
new OnMessageOptions() {
AutoComplete = false
});
// Now start sending
Task.Run(async () => {
int sent = 0;
while (!_cancelToken.IsCancellationRequested) {
var msg = new BrokeredMessage();
await _client.SendAsync(msg);
Console.WriteLine($"Sent {++sent}");
await Task.Delay(1000);
}
});
Console.ReadKey();
_cancelToken.Cancel();
}
private static QueueClient GetQueueClient(string queueName) {
var namespaceManager = NamespaceManager.CreateFromConnectionString(_serviceBusConnectionString);
if (!namespaceManager.QueueExists(queueName)) {
var settings = new QueueDescription(queueName);
settings.MaxDeliveryCount = 10;
settings.LockDuration = TimeSpan.FromSeconds(5);
settings.EnableExpress = true;
settings.EnablePartitioning = true;
namespaceManager.CreateQueue(settings);
}
var factory = MessagingFactory.CreateFromConnectionString(_serviceBusConnectionString);
factory.RetryPolicy = new RetryExponential(minBackoff: TimeSpan.FromSeconds(0.1), maxBackoff: TimeSpan.FromSeconds(30), maxRetryCount: 100);
var queueClient = factory.CreateQueueClient(queueName);
return queueClient;
}
}
I've tried playing around with settings but can't get this to work. Any ideas?
Answering my own question with response from Serkant Karaca # Microsoft here:
Very basic rule and I am not sure if this is documented. The received message needs to be processed in the callback function's life time. In your case, messages will be disposed when async callback completes, this is why your complete attempts are failing with ObjectDisposedException in another thread.
I don't really see how queuing messages for further processing helps on the throughput. This will add more burden to client for sure. Try processing the message in the async callback, that should be performant enough.
Bugger.