How do you create an async method? - c#

How do I make below simple method async so that i can it call it like await DoSomething
public void DoDomething()
{
string d = "doing something";
}
Editing Question for what I am actually doing in my method
public void RunValidationScripts()
{
string scriptDirPath = #"D:\ValidationScripts";
string[] psScriptsPath = Directory.GetFiles(scriptDirPath);
Dictionary<string, Collection<PSObject>> result = new Dictionary<string, Collection<PSObject>>();
foreach (string scriptPath in psScriptsPath)
{
result.Add(scriptPath, ExecutePSScript(scriptPath));
}
}
I am using package Microsoft.PowerShell.SDK
private Collection<PSObject> ExecutePSScript(string scriptFilePath)
{
using (var ps = PowerShell.Create())
{
string fileName = Path.GetFileName(scriptFilePath);
var results = ps.AddScript(File.ReadAllText(scriptFilePath)).Invoke();
return results;
}
}

The question is unclear. It looks like the actual problem is trying to execute PowerShell scripts asynchronously. Nothing will be gained by using Task.Run in this case. PowerShell scripts execute on a different thread already.
Powershell.InvokeAsync can be used to execute a PowerShell script asynchronously. Directory.EnumerateFiles can be used to start enumerating files and processing them without waiting for all files to be retrieved.
Assuming the method executing the scripts looks something like this:
async Task<IEnumerable<PSObject>> ExecutePSScript(string scriptPath)
{
var ps = PowerShell.Create();
var content=await File.ReadAllTextAsync(scriptPath);
ps.AddScript(content);
var results=await ps.InvokeAsync();
return results;
}
The calling method can be :
async Task RunValidationScriptsAsync(string folder)
{
var allResults=new List<PSObject>();
var files=Directory.EnumerateFiles(folder);
foreach(var file in files)
{
var results=await ExecutePSScript(file);
allResults.AddRange(results);
}
//Do something with the results
}

From a developer's perspective, there is one simple pratical rule of thumb that applies to most cases.
Invoking asynchronous APIs
The idea is that if you can choose between a synchronous and asynchronous API in the framework, you should opt for making all your code asynchronous and write your methods asynchronously, so that invocations to the aforementioned async framework methods is awaited.
If you don't utilize async methods, you should write synchronous methods as async does not provide any benefit

Why would you make simple method async, If there is nothing to wait for (IO bound or CPU bound operation). But if there is really a need (fire and forget kind of situation), You could do,
public async Task DoDomething()
{
string d=null;
await Task.Run(()=> {
d = "doing something";
}
}
Having an async is not important if you dont want to wait here, so,
public Task DoDomething()
{
string d=null;
Task.Run(()=> {
d = "doing something";
}
}
will work too.
Note: the question detail has changed significantly, and answer might not be relevant for this.

Related

Parallel.ForEach not adding items as expected in ConcurrentBag in C#

In my Asp.Net Core WebApi Controller, I'm receiving a IFormFile[] files. I need to convert this to of List<DocumentData>. I first used foreach. It was working fine. But later decided to change to Parallel.ForEach as I'm receiving many(>5) files.
Here is my DocumentData Class:
public class DocumentData
{
public byte[] BinaryData { get; set; }
public string FileName { get; set; }
}
Here is my Parallel.ForEach Logic:
var documents = new ConcurrentBag<DocumentData>();
Parallel.ForEach(files, async (currentFile) =>
{
if (currentFile.Length > 0)
{
using (var ms = new MemoryStream())
{
await currentFile.CopyToAsync(ms);
documents.Add(new DocumentData
{
BinaryData = ms.ToArray(),
FileName = currentFile.FileName
});
}
}
});
For Example, even for two files as input, documents always gives one file as output. Am I missing something?
I initially had List<DocumentData>. I found that it's not thread safe and changed to ConcurrentBag<DocumentData>. But still I'm getting unexpected results. Please assist on where I'm wrong?
I guess it is because, Parallel.Foreach doesn't support async/await. It only takes Action as input and executes it for each item. And in case of async delegates it will execute them in a fire-and-forget manner.
In that case passed lambda will be considered as async void function and async void can't be awaited.
If there were overload which takes Func<Task> then it would work.
I suggest you to create Tasks with the help of Select and use Task.WhenAll for executing them at the same time.
For example:
var tasks = files.Select(async currentFile =>
{
if (currentFile.Length > 0)
{
using (var ms = new MemoryStream())
{
await currentFile.CopyToAsync(ms);
documents.Add(new DocumentData
{
BinaryData = ms.ToArray(),
FileName = currentFile.FileName
});
}
}
});
await Task.WhenAll(tasks);
Additionally you can improve that code with just returning DocumentData instance from that method, and in such case there is no need to modify documents collection. Task.WhenAll has overload which takes IEnumerable<Task<TResult> as input and produces Task of TResult array. So, the result will be so:
var tasks = files.Select(async currentFile =>
{
if (currentFile.Length > 0)
{
using (var ms = new MemoryStream())
{
await currentFile.CopyToAsync(ms);
return new DocumentData
{
BinaryData = ms.ToArray(),
FileName = currentFile.FileName
};
}
}
return null;
});
var documents = (await Task.WhenAll(tasks)).Where(d => d != null).ToArray();
You had the right idea with a concurrent collection, but misused a TPL method.
In short you need to be very careful about async lambdas, and if you are passing them to an Action or Func<Task>
Your problem is because Parallel.For / ForEach is not suited for the async and await pattern or IO bound tasks. They are suited for cpu bound workloads. Which means they essentially have Action parameters and let's the task scheduler create the tasks for you
If you want to run mutple tasks at the same time use Task.WhenAll , or a TPL Dataflow ActionBlock which can deal effectively with both CPU bound and IO bound works loads, or said more directly, they can deal with tasks which is what an async method is.
The fundimental issue is when you call an async lambda on an Action, you are essentially creating an async void method, which will run as a task unobserved. That's to say, your TPL method is just creating a bunch of tasks in parallel to run a bunch of unobserved tasks and not waiting for them.
Think of it like this, you ask a bunch of friends to go and get you some groceries, they in turn tell someone else to get your groceries, yet your friends report back to you and say thier job is done. It obviously isn't and you have no groceries.

I cannot understand how exactly does await/async work

I started to look into Task, async/await concepts is c# and I'm having big problems understanding it, well at least i don't know how to implement it. I started rewriting an older test program i had written before, but now instead of threading i want to use these new concepts. Basically the layout is as it follows:
I have a simple class where i download the HTML content of a web page.
I process that in another class where i basically just parse the page to my model. Later on i want to display that to my UI.
The problem is that my program is not responsive, it blocks the UI while I'm processing the info.
I started learning this 2 days ago, i have read a lot of stuff online, including MSDN and some blogs but yet I'm unable to figure it out. Maybe someone can provide a look as well
HtmlDOwnloadCOde:
public async Task<string> GetMangaDescriptionPage(string detailUrl)
{
WebClient client = new WebClient();
Stream data = await client.OpenReadTaskAsync(detailUrl);
StreamReader reader = new StreamReader(data);
string s = reader.ReadToEnd();
data.Dispose();
reader.Dispose();
data.Close();
reader.Close();
return s;
}
My parse class code:
public async Task<MangaDetailsModel> ParseMangaDescriptionPage()
{
ParseOneManga pom = new ParseOneManga();
string t1 = await pom.GetMangaDescriptionPage(selectedManga.url);
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(t1);
var divs = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
x.Attributes["id"].Value.Contains("title")).ToArray();
mangaDetails.mangaName = divs[0].Element("h1").InnerText;
mangaDetails.description = divs[0].Descendants("p").Single().InnerText ?? "DSA";
var tds = divs[0].Descendants("td");
int info = 0;
var chapters = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
x.Attributes["id"].Value.Contains("chapters")).ToArray();
var chapterUi = chapters[0].Descendants("ul").Where(x => x.Attributes.Contains("class") &&
x.Attributes["class"].Value.Contains("chlist"));
foreach (var li in chapterUi)
{
var liChapter = li.Descendants("li");
foreach (var h3tag in liChapter)
{
var chapterH3 = h3tag.Descendants("a").ToArray();
SingleManagFox chapterData = new SingleManagFox();
chapterData.name = chapterH3[1].InnerHtml;
chapterData.url = chapterH3[1].GetAttributeValue("href", "0");
mangaDetails.chapters.Add(chapterData);
}
};
return mangaDetails;
}
UI code:
private async void mainBtn_Click(object sender, RoutedEventArgs e)
{
if (mangaList.SelectedItem != null)
{
test12((SingleManagFox)mangaList.SelectedItem);
}
}
private async void test12(SingleManagFox selectedManga)
{
selectedManga = (SingleManagFox)mangaList.SelectedItem;
MangaDetails mangaDetails = new MangaDetails(selectedManga);
MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
txtMangaArtist.Text = mdm.artisName;
txtMangaAuthor.Text = mdm.authorName;
chapterList.ItemsSource = mdm.chapters;
}
Sorry if its trivial but i cannot figure it out myself.
When going async you need to try to go async all the way and avoid mixing blocking calls with async calls.
You are using async void in the event handler with no await.
Try to avoid async void unless it is an event handler. test12 should be updated to return Task and awaited in the event handler mainBtn_Click.
private async void mainBtn_Click(object sender, RoutedEventArgs e) {
if (mangaList.SelectedItem != null) {
await test12((SingleManagFox)mangaList.SelectedItem);
}
}
private async Task test12(SingleManagFox selectedManga) {
selectedManga = (SingleManagFox)mangaList.SelectedItem;
MangaDetails mangaDetails = new MangaDetails(selectedManga);
MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
txtMangaArtist.Text = mdm.artisName;
txtMangaAuthor.Text = mdm.authorName;
chapterList.ItemsSource = mdm.chapters;
}
Also consider updating the web call to use HttpClient if available.
class ParseOneManga {
public async Task<string> GetMangaDescriptionPageAsync(string detailUrl) {
using (var client = new HttpClient()) {
string s = await client.GetStringAsync(detailUrl);
return s;
}
}
}
Reference: - Async/Await - Best Practices in Asynchronous Programming
Quite often people think that async-await means that multiple threads are processing your code at the same time. This is not the case, unless you explicitly start a different thread.
A good metaphore that helped me a lot explaining async-await is the restauran metaphor used in this interview with Eric Lippert. Search somewhere in the middle for async-await.
Eric Lipperts compares async-await processing with a cook who has to wait for his water to boil. Instead of waiting, he looks around if he can do other things instead. When finished doing the other thing, he comes back to see if the water is boiling and starts processing the boiling water.
The same is with your process. There is only one thread busy (at a time). This thread keeps processing until he has to await for something. This something is usually a fairly long process that is processed without using your CPU core, like writing a file to disk, loading a web page, or querying information from an external database.
Your thread can only do one thing at a time. So while it is busy calculating something, if can't react on operator input and your UI freezes, until the calculations are done. Async await will only help if there are a lot of times your thread would be waiting for other processes to complete
If you call an async function, you are certain that somewhere in that function is an await. In fact, if you declare your function async, and your forget to await in it, your compiler will warn you.
When your call meets the await in the function, your thread goes up its call stack to see if it can do other things. If you are not awaiting, you can continue processing, until you have to await. The thread goes up its call stack again to see if one of the callers is not awaiting etc.
async Task ReadDataAsync()
{
// do some preparations
using (TextReader textReader = ...)
{
var myReadTask = textReader.ReadToEndAsync();
// while the textReader is waiting for the information to be available
// you can do other things
ProcessSomething();
// after a while you really need the results from the read data,
// so you await for it.
string text = await MyReadTask;
// after the await, the results from ReatToEnd are available
Process(text);
...
There are some rules to follow:
an async function should return Task instead of void and Task<TResult> instead of TResult
There is one exception: the async event handler returns void instead of Task.
Inside your async function you should await somehow. If you don't await, it is useless to declare your function async
The result of await Task is void, and the result of await Task<TResult> is TResult
If you call an async function, see if you can do some processing instead of waiting for the results of the call
Note that even if you call several async functions before awaiting for them, does not mean that several threads are running these functions synchronously. The statement after your first call to the async function is processed after the called function starts awaiting.
async Task DoSomethingAsync()
{
var task1 = ReadAsync(...);
// no await, so next statement processes as soon as ReadAsync starts awaiting
DoSomeThingElse();
var task2 = QueryAsync(...);
// again no await
// now I need results from bothtask1, or from task2:
await Task.WhenAll(new Task[] {task1, task2});
var result1 = Task1.Result;
var result2 = Task2.Result;
Process(result1, result2);
...
Usually all your async functionality is performed by the same context. In practice this means that you can program as if your program is single threaded. This makes the look of your program much easier.
Another article that helped me a lot understanding async-await is Async-Await best practices written by the ever so helpful Stephen Cleary

Execute multiple CMD tasks and update asynchronously

I'm implementing a C# application. I need to execute a program on multiple remote machines at the same time. To do so I'm using PSExec over a CMD with multithreading. Basically, for each machine, I start a thread that starts a CMD process. Depending on the result of the program executed remotely I'd like to either take an action or kill it if it takes more than x minutes (hope that makes sense).
The issue I've got is that I don't really know how to control for how long the process has been running other than using WaitForExit and, that doesn't really let me go multi-threading as it waits till the CMD call has finished.
I'm sure there must be a way of doing this but I cannot really figure it out. Could anyone please help me?
Here is my code (I'm new at c# coding so might not be the best code, feel free to correct any part of it you consider it is not right):
public async void BulkExecution()
{
//Some code
foreach (string machine in Machines)
{
//more code to work out the CMDline and other duties.
var result = Task.Factory.StartNew(r => ExecutePsexec((string)r, RunBeforeKillMsec), CMDLine);
await result;
}
//More Code
}
private static void ExecutePsexec(string CMDline, int RunBeforeKillMsec)
{
Process compiler = new Process();
compiler.StartInfo.FileName = "psexec.exe";
compiler.StartInfo.Arguments = CMDline;
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.RedirectStandardOutput = true;
compiler.Start();
if (!compiler.WaitForExit(RunBeforeKillMsec))
{
ExecutePSKill(CMDline);
}
else
{
//Some Actions here
Common.Log(LogFile, CMDline.Split(' ')[0] + " finished successfully");
}
}
ExecutePsexec runs in a separate task. All such tasks are independent. await result; is what sequences them. You need to remove it.
Async void methods should be avoided. You should change the signature of the BulkExecution method to return a Task, so that you can await it and handle any exceptions that may occur. Inside the method create a Task for each machine, and then await all tasks with the Task.WhenAll method:
public async Task BulkExecution()
{
//Some code
Task[] tasks = Machines.Select(machine =>
{
//more code to work out the CMDline and other duties.
return Task.Run(r => ExecutePsexec(CMDLine, ExecutionTimeoutMsec));
}).ToArray();
await Task.WhenAll(tasks);
//More Code
}

Convert synchronous zip operation to async

We got an existing library where some of the methods needs to be converted to async methods.
However I'm not sure how to do it with the following method (errorhandling has been removed). The purpose of the method is to zip a file and save it to disk. (Note that the zip class doesn't expose any async methods.)
public static bool ZipAndSaveFile(string fileToPack, string archiveName, string outputDirectory)
{
var archiveNameAndPath = Path.Combine(outputDirectory, archiveName);
using (var zip = new ZipFile())
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zip.Comment = $"This archive was created at {System.DateTime.UtcNow.ToString("G")} (UTC)";
zip.AddFile(fileToPack);
zip.Save(archiveNameAndPath);
}
return true;
}
An implementation could look like this:
public static async Task<bool> ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory)
{
var archiveNameAndPath = Path.Combine(outputDirectory, archiveName);
await Task.Run(() =>
{
using (var zip = new ZipFile())
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zip.Comment = $"This archive was created at {System.DateTime.UtcNow.ToString("G")} (UTC)";
zip.AddFile(fileToPack);
zip.Save(archiveNameAndPath);
}
});
return true;
}
Which just seems wrong. The client could just call the sync method using Task.Run
Please, anyone got any hints on how to transform it into a async method ?
Which just seems wrong. The client could just call the sync method
using Task.Run
Spot on. By wrapping synchronous code in Task.Run() the library is now using the client's threadpool resources without it being readily apparent. Imagine what could happen to the client's threadpool if all libraries took this approach? Long story short, just expose the synchronous method and let the client decide if it wants to wrap it in Task.Run().
Having said that, if the ZipFile object had async functionality (e.g. had a SaveAsync() method) then you could make the outer method async as well. Here's an example of how that would look:
public static async Task<bool> ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory)
{
var archiveNameAndPath = Path.Combine(outputDirectory, archiveName);
using (var zip = new ZipFile())
{
// do stuff
await zip.SaveAsync(archiveNameAndPath);
}
return true;
}
As a temporarily solution, I would introduce an extension method:
public static class ZipFileExtensions
{
public static Task SaveAsync(this ZipFile zipFile, string filePath)
{
zipFile.Save(filePath);
return Task.FromResult(true);
}
}
Then the usage would be:
public static async Task<bool> ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory)
{
var archiveNameAndPath = Path.Combine(outputDirectory, archiveName);
using (var zip = new ZipFile())
{
...
await zip.SaveAsync(archiveNameAndPath).ConfugureAwait(false);
}
return true;
}
Implementing synchronous tasks does not violate anything (talking about Task.FromResult)
Submit a request to https://github.com/jstedfast/Ionic.Zlib asking for an async support in the library due to IO operations
Hope that's done eventually, and then you can upgrade the Ionic.Zlib in your app, delete the ZipFileExtensions, and continue using async version of the Save method (this time built into the library).
Alternatively, you can clone the repo from GitHub, and add SaveAsync by yourself, the submit a pull request back.
It's just not possible to 'convert' a sync method to an async if a library does not support it.
From performance standpoint, this might not be the best solution, but from management point of view, you can decouple stories "Convert everything to async" and "Improve app performance by having Ionic.Zlib async", what makes your backlog more granular.
public static Task<bool> ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory)
{
return Task.Run(() =>
{
var archiveNameAndPath = Path.Combine(outputDirectory, archiveName);
using (var zip = new ZipFile())
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zip.Comment = $"This archive was created at {System.DateTime.UtcNow.ToString("G")} (UTC)";
zip.AddFile(fileToPack);
zip.Save(archiveNameAndPath);
}
return true;
});
}
Then use like this
public async Task MyMethod()
{
bool b = await ZipAndSaveFileAsync();
}
Some of the answers suggest that zipping a file is not a process that you should do asynchronously. I don't agree with this.
I can imagine that zipping files is a process that might take some time. During this time you want to keep your UI responsive or you want to zip several files simultaneously, or you want to upload a zipped file while zipping the next one/
The code you show is the proper way to make your function asynchronous. You question whether it is useful to create such a small method. Why not let the users call Task.Run instead of call your async function?
The reason for this is called information hiding. By creating the async function you're hiding how you zip asynchronously, thus relieving others from knowing how to do this.
Besides, information hiding gives you the freedom to change the internals of the procedure as long as you don't change the pre- and postcondition.
One of the answers said that your function still is not asynchronous. That is not true. Callers of your function may call your async function without awaiting for it. While the task is zipping, the caller may do other things. As soon as it needs the boolean result of the task if can await for the task.
Example of usage:
private async Task DoSomethingSimultaneously()
{
var taskZipFileA = ZipAndSaveFileAsync(fileA, ...)
// while this task is zipping do other things,
// for instance start zipping file B:
var taskZipFileB = ZipAndSaveFileAsync(fileB, ...)
// while both tasks are zipping do other things
// after a while you want to wait until both files are finished:
await Task.WhenAll(new Task[] {taskZipFileA, taskZipFileB});
// after the await, the results are known:
if (taskZipFileA.Result)
{
// process the boolean result of taskZipFile A
}
Note the difference between Task.WaitAll and Task.WhenAll
In async - await you use Task.WhenAll. The return is a Task, so you can
await Task.WhenAll (...)
For proper async-await, all functions that call any async function need to be async themselves and return a Task (instead of void) or Task<TResult> instead of TResult. There is one exception: the event handler may return void.
private async void OnButton1_clicked(object sender, ...)
{
bool zipResult = await SaveAndZipFileAsync(...);
ProcessZipResult(zipResult);
}
Using this method your UI keeps responsive. You don't have to call Task.Run
If you have a non-async function and want to start zipping while doing something else, your non-async function has to call Task.Run. As the function is not async it can't use await. When it needs the result of task.Run it needs to use Task.Wait, or Task.WaitAll
private void NonAsyncZipper()
{
var taskZipFileA = Task.Run ( () => ZipAndSaveFileAsync(...);
// while zipping do other things
// after a while when the result is needed:
taskZipFileA.Wait();
ProcesZipResult(taskZipFileA.Result);
}
If it's possible to get the binary data from your Zip library after the compression, then instead of using this library to save the file, use .NET IO libraries to save it.
EDIT:
There is no point in using async for CPU bound operations (such as compression). In your case, the only benefit you can get from async is when you save the file to the disk. I thought that's what you were asking for.

AWAIT / ASYNC example of background worker returning List

I've seen general examples of how to use AWAIT/ ASYNC with the new 4.5 framework but no specific guidlines on how to replace the use of a background worker with the new constructs that dont in turn call any awaitable methods from the .net framework. how can you do so without using lambda expressions so a List can be returned? I'm thinking somthing like the following to free up the UI if main() is the UI thread (please forgive psudocode)
main()
{
List<string> resultSet = await CreateList(dirPath);
console.out(resultSet.ToString());
}
public async List<string> CreateList(string dirPath);
{
//do some work on dirPath NOT CALLING ANY ASYNC methods
return LIST<STRING>;
}
The reason you haven't seen examples of using async with synchronous code is because async is for asynchronous code.
That said, you can use Task.Run as an approximate replacement for BackgroundWorker. Task.Run enables you to take synchronous code and consume it in an asynchronous way:
main()
{
List<string> resultSet = await Task.Run(() => CreateList(dirPath));
console.out(resultSet.ToString());
}
public List<string> CreateList(string dirPath);
{
//do some work on dirPath NOT CALLING ANY ASYNC methods
return LIST<STRING>;
}
I'm currently going through a series on my blog on replacing BackgroundWorker with Task.Run, which you may find helpful.

Categories

Resources