What is wrong with my Code (SendPingAsync) - c#

Im writing a C# Ping-Application.
I started with a synchronous Ping-method, but I figurred out that pinging several server with one click takes more and more time.
So I decided to try the asynchronous method.
Can someone help me out?
public async Task<string> CustomPing(string ip, int amountOfPackets, int sizeOfPackets)
{
// timeout
int Timeout = 2000;
// PaketSize logic
string packet = "";
for (int j = 0; j < sizeOfPackets; j++)
{
packet += "b";
};
byte[] buffer = Encoding.ASCII.GetBytes(packet);
// time-var
long ms = 0;
// Main Method
using (Ping ping = new Ping())
for (int i = 0; i < amountOfPackets; i++)
{
PingReply reply = await ping.SendPingAsync(ip, Timeout, buffer);
ms += reply.RoundtripTime;
};
return (ms / amountOfPackets + " ms");
};
I defined a "Server"-Class (Ip or host, City, Country).
Then I create a "server"-List:
List<Server> ServerList = new List<Server>()
{
new Server("www.google.de", "Some City,", "Some Country")
};
Then I loop through this list and I try to call the method like this:
foreach (var server in ServerList)
ListBox.Items.Add("The average response time of your custom server is: " + server.CustomPing(server.IP, amountOfPackets, sizeOfPackets));
Unfortunately, this is much more competitive than the synchronous method, and at the point where my method should return the value, it returns
System.Threading.Tasks.Taks`1[System.string]

since you have an async method it will return the task when it is called like this:
Task<string> task = server.CustomPing(server.IP, amountOfPackets, sizeOfPackets);
when you add it directly to your ListBox while concatenating it with a string it will use the ToString method, which by default prints the full class name of the object. This should explaint your output:
System.Threading.Tasks.Taks`1[System.string]
The [System.string] part actually tells you the return type of the task result. This is what you want, and to get it you would need to await it! like this:
foreach (var server in ServerList)
ListBox.Items.Add("The average response time of your custom server is: " + await server.CustomPing(server.IP, amountOfPackets, sizeOfPackets));
1) this has to be done in another async method and
2) this will mess up all the parallelity that you are aiming for. Because it will wait for each method call to finish.
What you can do is to start all tasks one after the other, collect the returning tasks and wait for all of them to finish. Preferably you would do this in an async method like a clickhandler:
private async void Button1_Click(object sender, EventArgs e)
{
Task<string> [] allTasks = ServerList.Select(server => server.CustomPing(server.IP, amountOfPackets, sizeOfPackets)).ToArray();
// WhenAll will wait for all tasks to finish and return the return values of each method call
string [] results = await Task.WhenAll(allTasks);
// now you can execute your loop and display the results:
foreach (var result in results)
{
ListBox.Items.Add(result);
}
}

The class System.Threading.Tasks.Task<TResult> is a helper class for Multitasking. While it resides in the Threading Namespace, it works for Threadless Multitasking just as well. Indeed if you see a function return a task, you can usually use it for any form of Multitasking. Tasks are very agnostic in how they are used. You can even run it synchronously, if you do not mind that little extra overhead of having a Task doing not a lot.
Task helps with some of the most important rules/convetions of Multitasking:
Do not accidentally swallow exceptions. Threadbase Multitasking is notoriously good in doing just that.
Do not use the result after a cancelation
It does that by throwing you exceptions in your face (usually the Aggregate one) if you try to access the Result Property when convention tells us you should not do that.
As well as having all those other usefull properties for Multitasking.

Related

Stopwatch with an async action which awaits calls from a database

How can I properly use the Stopwatch class with an async action event that awaits calls to my database asynchronously inside an async Post method on DotNet Core ?
Why
To time my code and check for bottleneck. This is a simplified test method that will contain more code as time goes on.
Errors
I tried using an Action event, a Task event and a Func<Task> event without success and they all give me errors which always occurs when I am awaiting a call from my database asynchronously using EF Core
When I use Action event
An unhandled exception of type 'System.ObjectDisposedException' occurred in System.Private.CoreLib.dll. Cannot access a disposed context instance.
When I use Func<Task>
System.Threading.Tasks.TaskCanceledException: A task was canceled.
It doesn't print anything when I use Task event and the rest of the code executes without errors
Code
Post Method
public async Task<JsonResult> OnPostTest() {
// my Database Context
using DatabaseContext dc = _dbContext;
// list of json data that will be returned back to the client
List<object>? listJsonData = null;
// stopwatch initialization
Stopwatch sw = new();
sw.LogActionAsync(nameof(OnPostTest), (async () => { // new Task(async () => { // new Func<Task>(async() => {
// get list of data from TableTest database with a valid name and are not marked as delete
List<TableTest> listValidTabletest = await dc.ListTest.AsNoTracking().Where(t => !string.IsNullOrWhiteSpaces(t.strName) && !t.blnDelete).ToListAsync(); //<-- returns a list asynchronously and where the error occurs
// initialize list that will be returned
listJsonData = new();
foreach (TableTest t in listValidTableTest) {
// object that will be in the list of returned json objects
var returnData = new {
t.strName,
t.arrPrices,
t.strStartDate,
t.strEndDate
};
listJsonData.Add(returnData);
}
}));
return new JsonResult(new {
// return list or an empty array if list has not been initialized
arrJsonData = listJsonData?.toArray() ?? Array.Empty<object>(),
blnGetStatus = bool.TrueString
});
}
Stopwatch Extension Class
public static async void LogActionAsync(this Stopwatch sw, string? strMethodName, Action asyncAction, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
List<Task> listOfTasks = new();
for (int i = 0; i < intNbOfIterations; i++) {
listOfTasks.Add(Task.Factory.StartNew(asyncAction)); // for Action event
//listOfTask.Add(asyncTask); // for Task event
}
await Task.WhenAll(listOfTasks);
//await asyncFuncTask; // for Func\<Task> event
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
}
EDIT:
I changed my stopwatch extension method to async Task LogActionAsync... and my stopwatch object to await sw.LogActionAsync... but now nothing is being logged*. Any idea ?
There's a lot of bugs in this code. To summarize:
async void in two places.
Missing awaits.
Using a single database context concurrently.
Adding to a list of results concurrently.
So, let's fix these one by one.
async void in two places.
Missing awaits.
As another answer noted, LogActionAsync should not be async void but should be async Task and awaited.
I changed my stopwatch extension method to async Task LogActionAsync... and my stopwatch object to await sw.LogActionAsync...
You're still missing one more async void. It's a tricky one: lambdas, when assigned to Action variables, become async void. The proper delegate type for an asynchronous method without a return value is Func<Task>, not Action.
Code:
public static async Task LogActionAsync(this Stopwatch sw, string? strMethodName, Func<Task> asyncAction, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
List<Task> listOfTasks = new();
for (int i = 0; i < intNbOfIterations; i++) {
listOfTasks.Add(asyncAction());
}
await Task.WhenAll(listOfTasks);
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
}
And now you can properly use await everywhere.
Using a single database context concurrently.
Adding to a list of results concurrently.
As another answer noted, you will need one database context per action lambda. This is a limitation of Entity Framework (in turn imposed by a limitation of most SQL on-the-wire protocols).
The List<T>.Add method is also not threadsafe, and the code is potentially invoking it from multiple threads concurrently. It's possible to use a concurrent collection, but it's easier and cleaner to return result data instead of modifying a shared collection as a side effect.
But, really, I suspect that the concurrency in the posted code is an accident. It seems very odd to run N "iterations" of something concurrently when doing timing; I believe the desired semantics are to run N iterations of something serially.
If my assumption is correct, then the code should look like this:
public static async Task LogActionAsync(this Stopwatch sw, string? strMethodName, Func<Task> asyncAction, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
for (int i = 0; i < intNbOfIterations; i++) {
await asyncAction();
}
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
}
public static async Task<T> LogActionAsync<T>(this Stopwatch sw, string? strMethodName, Func<Task<T>> asyncFunc, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
T result = default;
for (int i = 0; i < intNbOfIterations; i++) {
result = await asyncFunc();
}
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
return result;
}
public async Task<JsonResult> OnPostTest() {
// my Database Context
using DatabaseContext dc = _dbContext;
// list of json data that will be returned back to the client
List<object>? listJsonData = null;
// stopwatch initialization
Stopwatch sw = new();
listJsonData = await sw.LogActionAsync(nameof(OnPostTest), (async () => {
// get list of data from TableTest database with a valid name and are not marked as delete
List<TableTest> listValidTabletest = await dc.ListTest.AsNoTracking().Where(t => !string.IsNullOrWhiteSpaces(t.strName) && !t.blnDelete).ToListAsync();
// initialize list that will be returned
var jsonData = new List<object>();
foreach (TableTest t in listValidTableTest) {
// object that will be in the list of returned json objects
var returnData = new {
t.strName,
t.arrPrices,
t.strStartDate,
t.strEndDate
};
jsonData.Add(returnData);
}
return jsonData;
}));
return new JsonResult(new {
// return list or an empty array if list has not been initialized
arrJsonData = listJsonData?.toArray() ?? Array.Empty<object>(),
blnGetStatus = bool.TrueString
});
}
You're not awaiting your call to LogActionAsync, so your call happens after your page action is over, which is why you get all those disposed exceptions. Your entire page and all its DI objects and database contexts and everything have long been disposed by that point.
async void should be considered a debugging tool, it helps find any async issue inexperienced people make right away!
The problem in your code has nothing to do with StopWatch and everything to do with entity framework.
Entity Framework DbContext is not concurrent safe.
You need to move the creation and disposal of the DbContext inside the Task.
Additionally, you should not be using Task.Factory.StartNew due to weird exception handling. And in this case, you should not use Task.Run nor Task.Factory.StartNew because you do not need a thread for concurrency.

How do you set up some function to run in a background thread but keep the normal return type instead of the Task<int>?

I have a function from the one service to that will get the count of all files inside a directory. And another service will get that int number to do some stuff with it.
public int GetNumberOfAvatarsInFile()
{
try
{
var path = GetAvatarsFilePath();
var numberOfAvatars = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly).Length;
return numberOfAvatars;
}
catch (Exception exception)
{
var message = $"Error While Getting Total Numbers Of Avatars at {DateTime.Now}\n\t" +
$"Error: {JsonConvert.SerializeObject(exception)}";
sentryClient.CaptureMessage(message, SentryLevel.Error);
return 1;
}
}
private string GetAvatarsFilePath()
{
var webRootPath = webHostEnvironment.WebRootPath;
var path = Path.Combine(webRootPath, "path");
return path;
}
The other service will use this function like this
private int GetMaximumAvatarId() => avatarService.GetNumberOfAvatarsInFile();
How do I set up so that all these file getting logic and string combine will be separated to a background thread/another thread by either Task.Run or something similar?
When I try to set up the GetNumberOfAvatarsInFile() by implementing await Task.Run(async () => LOGIC+Return int);
I have to return a Task rather than the int from the other service that is calling it as well, which is not desirable since those are other people code and I should not change them. Also as far as I know all the Path.Combine and Directory functions do not employ any awaiter.
Is there a way to implement this?
As mentioned in the comments, the best practice is to provide async methods to the caller and use async all the way (see this article). However there are 2 things that can already be done:
1. Make your I/O method run asynchronously in a separate thread.
2. Have callers call your method asynchronously even if the implementation is synchronous.
The implementations on client side and on service side are independent. Here is a commented example that I hope shows how to do this. Most of the code below is unnecessary and is there only to illustrate what happens when multiple callers call your method and what is executed when. You may change the Thread.Sleep() values to simulate different execution time.
I also added a side note regarding the value you return in the Exception, that does not look ok to me.
public class Program
{
public static void Main()
{
// These simulate 3 callers calling your service at different times.
var t1 = Task.Run(() => GetMaximumAvatarId(1));
Thread.Sleep(100);
var t2 = Task.Run(() => GetMaximumAvatarId(2));
Thread.Sleep(2000);
var t3 = Task.Run(() => GetMaximumAvatarId(3));
// Example purposes.
Task.WaitAll(t1, t2, t3);
Console.WriteLine("MAIN: Done.");
Console.ReadKey();
}
// This is a synchronous call on the client side. This could very well be implemented
// as an asynchronous call, even if the service method is synchronous, by using a
// Task and having the caller await for it (GetMaximumAvatarIdAsync).
public static int GetMaximumAvatarId(int callerId)
{
Console.WriteLine($"CALLER {callerId}: Calling...");
var i = GetNumberOfAvatarsInFile(callerId);
Console.WriteLine($"CALLER {callerId}: Done -> there are {i} files.");
return i;
}
// This method has the same signature as yours. It's synchronous in the sense that it
// does not return an awaitable. However it now uses `Task.Run` in order to execute
// `Directory.GetFiles` in a threadpool thread, which allows to run other code in
// parallel (in this example `Sleep` calls, in real life useful code). It finally
// blocks waiting for the result of the task, then returns it to the caller as an int.
// The "callerId" is for the example only, you may remove this everywhere.
public static int GetNumberOfAvatarsInFile(int callerId)
{
Console.WriteLine($" SERVICE: Called by {callerId}...");
var path = GetAvatarsFilePath();
var t = Task.Run(() => Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly).Length);
// Simulate long work for a caller, showing the caller.
Console.WriteLine($" SERVICE: Working for {callerId}...");
Thread.Sleep(500);
Console.WriteLine($" SERVICE: Working for {callerId}...");
Thread.Sleep(500);
Console.WriteLine($" SERVICE: Working for {callerId}...");
Thread.Sleep(500);
Console.WriteLine($" SERVICE: Blocking for {callerId} until task completes.");
return t.Result; // Returns an int.
// --------------------------------------------------------
// Side note: you should return `-1` in the `Exception`.
// Otherwise it is impossible for the caller to know if there was an error or
// if there is 1 avatar in the file.
// --------------------------------------------------------
}
// Unchanged.
private string GetAvatarsFilePath()
{
var webRootPath = webHostEnvironment.WebRootPath;
var path = Path.Combine(webRootPath, "path");
return path;
}
}

Async method not working parallel? Simple Iteration inside both Async method, Please suggest How to make it run parallel?

I am trying to figure it out that why these two Async method not working parallel.
Note that each method returns some html, and for this Iteration inside it (its demo code) (original code is in my .Net project).
Each method individually takes 9 seconds to complete.
Both method are Async even it takes 18 seconds to complete if its invoke and run in single method. It means it not runs parallel.
Overall what i want is, It should take 9 second to complete both the method.
How it is possible? Please suggest to run both method parallel.
(I want this because in my project there are many method inside one method, i want all runs parallel)
Thanks in advance.
Please review below code:
namespace LearnAynchStackOverflow
{
class Program
{
static async Task Main(string[] args)
{
var startTime = DateTime.Now;
Console.WriteLine($"Start time: {startTime}");
var emailHtml = EmailTemplate.EmailHtml();
var smsHtml = SmsTemplate.SmsHtml();
var f = await Task.WhenAll(emailHtml, smsHtml);
var endTime = DateTime.Now;
Console.WriteLine($"End time: {endTime}");
Console.ReadLine();
}
}
public class EmailTemplate
{
//This method takes 9 seconds to complete
public static async Task<string> EmailHtml()
{
string str = string.Empty;
for (int i = 0; i < 25000; i++)
{
str += "html1" + i + " ";
str += "html1" + i + " ";
}
var a = await Task.FromResult(str);
return a;
}
}
public class SmsTemplate
{
//This method takes 9 seconds to complete
public static async Task<string> SmsHtml()
{
string str = string.Empty;
for (int i = 0; i < 25000; i++)
{
str += "html2" + i + " ";
str += "html2" + i + " ";
}
var z = await Task.FromResult(str);
return z;
}
}
}
And Below is my console result which takes 18 seconds:
Your methods are asynchronous only on paper. They both always complete synchronously, because await Task.FromResult(str) completes immediately without any asynchronous operations. Both calls:
var emailHtml = EmailTemplate.EmailHtml();
var smsHtml = SmsTemplate.SmsHtml();
run the loop synchronously and return a completed task. Then the Task.WhenAll also returns a completed task. There is never any asynchrony, no continuations, only plain old sequential execution.
Making your method async doesn't magically cause it to be ran on a separate thread, actually, There Is No Thread. Your task has nothing to do with asynchronous operations. If you want to run CPU-bound tasks on the thread pool, use Task.Run
public class EmailTemplate
{
public static string EmailHtml()
{
string str = string.Empty;
for (int i = 0; i < 25000; i++)
{
str += "html1" + i + " ";
str += "html1" + i + " ";
}
return str;
}
}
public class SmsTemplate
{
public static string SmsHtml()
{
string str = string.Empty;
for (int i = 0; i < 25000; i++)
{
str += "html2" + i + " ";
str += "html2" + i + " ";
}
return str;
}
}
class Program
{
static async Task Main(string[] args)
{
var startTime = DateTime.Now;
Console.WriteLine($"Start time: {startTime}");
var emailHtml = Task.Run(() => EmailTemplate.EmailHtml());
var smsHtml = Task.Run(() => SmsTemplate.SmsHtml());
var f = await Task.WhenAll(emailHtml, smsHtml);
var endTime = DateTime.Now;
Console.WriteLine($"End time: {endTime}");
Console.ReadLine();
}
}
As an aside, don't you think that concatenating a string 25000 times taking 9 seconds is a bit pathological? I do, that's why I'd use a StringBuilder.
public static string EmailHtml()
{
var stringBuilder = new StringBuilder();
for (int i = 0; i < 25000; i++)
{
stringBuilder.Append("html1").Append(i).Append(" ");
// I don't know if this duplication is intentional, but I left it in case it was.
stringBuilder.Append("html1").Append(i).Append(" ");
}
return stringBuilder.ToString();
}
Your sample code runs synchronously. To understand why, you need to understand how asynchronous methods work.
All asynchronous methods start running synchronously - just like any other method. The magic happens at the first await that acts on an incomplete Task. At that point, the method returns its own incomplete Task that the calling method can use to know when the task has actually finished.
Your sample code never returns an incomplete Task. Task.FromResult returns a completed Task. When await sees a completed Task, your method continues running synchronously, just like any other method.
So when you do this:
var emailHtml = EmailTemplate.EmailHtml();
var smsHtml = SmsTemplate.SmsHtml();
Your call to SmsHtml() doesn't even start until EmailHtml() has fully completed.
If you are using await Task.FromResult to try to simulate some asynchronous work (like actually sending an SMS or email), then that won't work. You can use Task.Delay() instead. That will return an incomplete Task that will resolve when the timer runs out.
See if you get different results using this code:
public class EmailTemplate
{
//This method takes 9 seconds to complete
public static async Task<string> EmailHtml()
{
string str = string.Empty;
for (int i = 0; i < 25000; i++)
{
str += "html1" + i + " ";
str += "html1" + i + " ";
}
await Task.Delay(9000); //wait 9 seconds asynchronously
return str;
}
}
public class SmsTemplate
{
//This method takes 9 seconds to complete
public static async Task<string> SmsHtml()
{
string str = string.Empty;
for (int i = 0; i < 25000; i++)
{
str += "html2" + i + " ";
str += "html2" + i + " ";
}
await Task.Delay(9000); //wait 9 seconds asynchronously
return str;
}
}
Your code in Main is correct for what you're trying to do. But just note that asynchronous does not mean parallel:
Parallel means two lines of code are being evaluated simultaneously (on different threads)
Asynchronous means releasing the current thread while you wait for some external thing to happen. This allows you to start some other operation while you wait.
Parallel is about how your code runs. Asynchronous is about how your code waits.
In the changed code I gave, the initial parts of each method will never run in parallel. EmailHtml() will run up until the await, when it returns a Task. Then SmsHtml() will start running, up until the await. That will happen one after the other.
The continuations of those methods (everything after await) might run in parallel (on different threads) depending on the type of application.
Microsoft has a series of well-written articles about Asynchronous programming with async and await that are worth reading. It should help you understand better how all this works.
I have just copied and run your code and it completes in 7 seconds. It seems to be correct. Your results may differ according to many reasons. What kind of app are you running (ASP.NET, Console)? What your PC configuration is? What version of .NET are you using?
Async keyword makes the Task inside the block run in parallel and all other sync blocks run sequentially. Making the method async will never make the code async.
What does async keyword do?
public static async Task<string> EmailHtml()
{
stmt1;
(task)stmt2;
stmt3;
(task)stmt4;
}
Here, EmailHtml() starts running sequentially, when it encounters a task (stmt2, stmt4) a new thread is taken from thread pool and will run in background (will never be merged to the main thread until it is awaited) and other stmts (1 & 3) will be sequential.
How to make the code run in parallel?
Place the code inside SmsHtml and EmailHtml inside Task.Run or Task.Factory.StartNew (will explicitly ask to run the block inside a new thread) according to your use case. Now, these two methods run in a different thread and the rest of your code will make it run in 9 seconds.
public static async Task<string> EmailHtml()
{
return Task.Run(() => {return str;});
}
Note: All the tasks mentioned here are assumed to be running aync

Task.WaitAll deadlock

I have a question regarding Task.WaitAll. At first I tried to use async/await to get something like this:
private async Task ReadImagesAsync(string HTMLtag)
{
await Task.Run(() =>
{
ReadImages(HTMLtag);
});
}
Content of this function doesn't matter, it works synchronously and is completely independent from outside world.
I use it like this:
private void Execute()
{
string tags = ConfigurationManager.AppSettings["HTMLTags"];
var cursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
List<Task> tasks = new List<Task>();
foreach (string tag in tags.Split(';'))
{
tasks.Add(ReadImagesAsync(tag));
//tasks.Add(Task.Run(() => ReadImages(tag)));
}
Task.WaitAll(tasks.ToArray());
Mouse.OverrideCursor = cursor;
}
Unfortunately I get deadlock on Task.WaitAll if I use it that way (with async/await). My functions do their jobs (so they are executed properly), but Task.WaitAll just stays here forever because apparently ReadImagesAsync doesn't return to the caller.
The commented line is approach that actually works correctly. If I comment the tasks.Add(ReadImagesAsync(tag)); line and use tasks.Add(Task.Run(() => ReadImages(tag))); - everything works well.
What am I missing here?
ReadImages method looks like that:
private void ReadImages (string HTMLtag)
{
string section = HTMLtag.Split(':')[0];
string tag = HTMLtag.Split(':')[1];
List<string> UsedAdresses = new List<string>();
var webClient = new WebClient();
string page = webClient.DownloadString(Link);
var siteParsed = Link.Split('/');
string site = $"{siteParsed[0]} + // + {siteParsed[1]} + {siteParsed[2]}";
int.TryParse(MinHeight, out int minHeight);
int.TryParse(MinWidth, out int minWidth);
int index = 0;
while (index < page.Length)
{
int startSection = page.IndexOf("<" + section, index);
if (startSection < 0)
break;
int endSection = page.IndexOf(">", startSection) + 1;
index = endSection;
string imgSection = page.Substring(startSection, endSection - startSection);
int imgLinkStart = imgSection.IndexOf(tag + "=\"") + tag.Length + 2;
if (imgLinkStart < 0 || imgLinkStart > imgSection.Length)
continue;
int imgLinkEnd = imgSection.IndexOf("\"", imgLinkStart);
if (imgLinkEnd < 0)
continue;
string imgAdress = imgSection.Substring(imgLinkStart, imgLinkEnd - imgLinkStart);
string format = null;
foreach (var imgFormat in ConfigurationManager.AppSettings["ImgFormats"].Split(';'))
{
if (imgAdress.IndexOf(imgFormat) > 0)
{
format = imgFormat;
break;
}
}
// not an image
if (format == null)
continue;
// some internal resource, but we can try to get it anyways
if (!imgAdress.StartsWith("http"))
imgAdress = site + imgAdress;
string imgName = imgAdress.Split('/').Last();
if (!UsedAdresses.Contains(imgAdress))
{
try
{
Bitmap pic = new Bitmap(webClient.OpenRead(imgAdress));
if (pic.Width > minHeight && pic.Height > minWidth)
webClient.DownloadFile(imgAdress, SaveAdress + "\\" + imgName);
}
catch { }
finally
{
UsedAdresses.Add(imgAdress);
}
}
}
}
You are synchronously waiting for tasks to finish. This is not gonna work for WPF without a little bit of ConfigureAwait(false) magic. Here is a better solution:
private async Task Execute()
{
string tags = ConfigurationManager.AppSettings["HTMLTags"];
var cursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
List<Task> tasks = new List<Task>();
foreach (string tag in tags.Split(';'))
{
tasks.Add(ReadImagesAsync(tag));
//tasks.Add(Task.Run(() => ReadImages(tag)));
}
await Task.WhenAll(tasks.ToArray());
Mouse.OverrideCursor = cursor;
}
If this is WPF, then I'm sure you would call it when some kind of event happens. The way you should call this method is from event handler, e.g.:
private async void OnWindowOpened(object sender, EventArgs args)
{
await Execute();
}
Looking at the edited version of your question I can see that in fact you can make it all very nice and pretty by using async version of DownloadStringAsync:
private async Task ReadImages (string HTMLtag)
{
string section = HTMLtag.Split(':')[0];
string tag = HTMLtag.Split(':')[1];
List<string> UsedAdresses = new List<string>();
var webClient = new WebClient();
string page = await webClient.DownloadStringAsync(Link);
//...
}
Now, what's the deal with tasks.Add(Task.Run(() => ReadImages(tag)));?
This requires knowledge of SynchronizationContext. When you create a task, you copy the state of thread that scheduled the task, so you can come back to it when you are finished with await. When you call method without Task.Run, you say "I want to come back to UI thread". This is not possible, because UI thread is already waiting for the task and so they are both waiting for themselves. When you add another task to the mix, you are saying: "UI thread must schedule an 'outer' task that will schedule another, 'inner' task, that I will come back to."
Use WhenAll instead of WaitAll, Turn your Execute into async Task and await the task returned by Task.WhenAll.
This way it never blocks on an asynchronous code.
I found some more detailed articles explaining why actually deadlock happened here:
https://medium.com/bynder-tech/c-why-you-should-use-configureawait-false-in-your-library-code-d7837dce3d7f
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Short answer would be making a small change in my async method so it looks like that:
private async Task ReadImagesAsync(string HTMLtag)
{
await Task.Run(() =>
{
ReadImages(HTMLtag);
}).ConfigureAwait(false);
}
Yup. That's it. Suddenly it doesn't deadlock. But these two articles + #FCin response explain WHY it actually happened.
It's like you are saying i do not care when ReadImagesAsync() finishes but you have to wait for it .... Here is a definition
The Task.WaitAll blocks the current thread until all other tasks have completed execution.
The Task.WhenAll method is used to create a task that will complete if and only if all the other tasks are complete.
So, if you are using Task.WhenAll you would get a task object that isn't complete. However, it will not block and would allow the program to execute. On the contrary, the Task.WaitAll method call actually blocks and waits for all other tasks to complete.
Essentially, Task.WhenAll would provide you a task that isn't complete but you can use ContinueWith as soon as the specified tasks have completed their execution. Note that neither the Task.WhenAll method nor the Task.WaitAll would run the tasks, i.e., no tasks are started by any of these methods.
Task.WhenAll(taskList).ContinueWith(t => {
// write your code here
});

How to use Task.WhenAny with ReadLineAsync to get data from any TcpClient

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.

Categories