How to Asynchronously Call Static Methods - c#

I am trying to construct a simple class, which calls a reboot function depending on the machine type to be rebooted. The called methods refer to a library which contains public static methods. I want to asynchronously call these static methods using Task in order to call the reboot methods in parallel. Here is the code so far:
EDIT
Following the community's request, this is now a version of the same question, with the code below compiling. Please not that you need the Renci.SshNet lib, and also need to set references to it in your project.
// libs
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using Renci.SshNet;
namespace ConsoleApp
{
class Program
{
// Simple Host class
public class CHost
{
public string IP;
public string HostType;
public CHost(string inType, string inIP)
{// constructor
this.IP = inIP;
this.HostType = inType;
}
}
// Call test function
static void Main(string[] args)
{
// Create a set of hosts
var HostList = new List<CHost>();
HostList.Add( new CHost("Machine1", "10.52.0.93"));
HostList.Add( new CHost("Machine1", "10.52.0.30"));
HostList.Add( new CHost("Machine2", "10.52.0.34"));
// Call async host reboot call
RebootMachines(HostList);
}
// Reboot method
public static async void RebootMachines(List<CHost> iHosts)
{
// Locals
var tasks = new List<Task>();
// Build list of Reboot calls - as a List of Tasks
foreach(var host in iHosts)
{
if (host.HostType == "Machine1")
{// machine type 1
var task = CallRestartMachine1(host.IP);
tasks.Add(task); // Add task to task list
}
else if (host.HostType == "Machine2")
{// machine type 2
var task = CallRestartMachine2(host.IP);
tasks.Add(task); // Add task to task list
}
}
// Run all tasks in task list in parallel
await Task.WhenAll(tasks);
}
// ASYNC METHODS until here
private static async Task CallRestartMachine1(string host)
{// helper method: reboot machines of type 1
// The compiler complains here (RebootByWritingAFile is a static method)
// Error: "This methods lacks await operators and will run synchronously..."
RebootByWritingAFile(#"D:\RebootMe.bm","reboot");
}
private static async Task CallRestartMachine2(string host)
{// helper method: reboot machines of type 2
// The compiler warns here (RebootByWritingAFile is a static method)
// Error: "This methods lacks await operators and will run synchronously..."
RebootByNetwork(host,"user","pwd");
}
// STATIC METHODS here, going forward
private static void RebootByWritingAFile(string inPath, string inText)
{// This method does a lot of checks using more static methods, but then only writes a file
try
{
File.WriteAllText(inPath, inText); // static m
}
catch
{
// do nothing for now
}
}
private static void RebootByNetwork(string host, string user, string pass)
{
// Locals
string rawASIC = "";
SshClient SSHclient;
SshCommand SSHcmd;
// Send reboot command to linux machine
try
{
SSHclient = new SshClient(host, 22, user, pass);
SSHclient.Connect();
SSHcmd = SSHclient.RunCommand("exec /sbin/reboot");
rawASIC = SSHcmd.Result.ToString();
SSHclient.Disconnect();
SSHclient.Dispose();
}
catch
{
// do nothing for now
}
}
}
}
My only problem with this setup so far is that the static methods are called immediately (sequentially) and not assigned to a task. For example the line
...
else if (host.HostType == "Machine2")
{// machine type 2
var task = CallRestartMachine2(host.IP);
tasks.Add(task); // Add task to task list
}
...
takes 20 seconds to execute if the host is unreachable. If 10 hosts are unreachable the sequential duration is 20*10 = 200 seconds.
I am aware of some seemingly similar questions such as
c# asynchronously call method
Asynchronous call with a static method in C# .NET 2.0
How to call a method asynchronously
Simple Async Await Example for Asynchronous Programming
However, the cited lambda expressions still leave me with the same compiler error ["This methods lacks await operators..."]. Also, I do not want to spawn explicit threads (new Thread(() => ...)) due to high overhead if restarting a large number of machine in a cluster.
I may need to reboot a large number of machines in a cluster. Hence my question: How can I change my construct in order to be able to call the above static methods in parallel?
EDIT
Thanks to the comments of #JohanP and #MickyD, I would like to elaborate that I have actually tried writing the async version of both static methods. However that sends me down a rabbit hole, where every time a static method is called within the async method I get the compiler warning that the call will be synchronous. Here is an example of how I tried to wrap the call to method as an async task, hoping to call the dependent methods in an async manner.
private static async Task CallRestartMachine1(string host)
{// helper method: reboot machines of type 1
// in this version, compiler underlines '=>' and states that
// method is still called synchronously
var test = await Task.Run(async () =>
{
RebootByWritingAFile(host);
});
}
Is there a way to wrap the static method call such that all static child methods don't all need to rewritten as async?
Thank you all in advance.

Your code has a strange mix of async with continuations and it won't even compile. You need to make it async all the way up. When you call RebootMachines(...) and that call can't be awaited, you can schedule continuations on that i.e. RebootMachines(...).ContinueWith(t=> Console.WriteLine('All Done'))
public static async Task RebootMachines(List<CHost> iHosts)
{
var tasks = new List<Task>();
// Build list of Reboot calls - as a List of Tasks
foreach(var host in iHosts)
{
if (host.HostType == "Machine1")
{// machine type 1
task = CallRestartMachine1(host.IP);
}
else if (host.HostType == "Machine2")
{// machine type 2
task = CallRestartMachine2(host.IP);
}
// Add task to task list - for subsequent parallel processing
tasks.Add(task);
}
// Run all tasks in task list in parallel
await Task.WhenAll(tasks);
}
private static async Task CallRestartMachine1(string host)
{// helper method: reboot machines of type 1
//RebootByWritingAFile is method that returns a Task, you need to await it
// that is why the compiler is warning you
await RebootByWritingAFile(host);
}
private static async Task CallRestartMachine2(string host)
{// helper method: reboot machines of type 2
await RebootByNetwork(host);
}

Everyone, thanks for your input and your help. I have played around with his over the last couple of days, and have come up with the following way to asynchronously call a static method:
// libs
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using Renci.SshNet;
namespace ConsoleApp
{
class Program
{
// Simple Host class
public class CHost
{
public string IP;
public string HostType;
public CHost(string inType, string inIP)
{// constructor
this.IP = inIP;
this.HostType = inType;
}
}
// Call test function
static void Main(string[] args)
{
// Create a set of hosts
var HostList = new List<CHost>();
HostList.Add( new CHost("Machine1", "10.52.0.93"));
HostList.Add( new CHost("Machine1", "10.52.0.30"));
HostList.Add( new CHost("Machine2", "10.52.0.34"));
// Call async host reboot call
RebootMachines(HostList);
}
// Reboot method
public static async void RebootMachines(List<CHost> iHosts)
{
// Locals
var tasks = new List<Task>();
// Build list of Reboot calls - as a List of Tasks
foreach(var host in iHosts)
{
if (host.HostType == "Machine1")
{// machine type 1
var task = CallRestartMachine1(host.IP);
tasks.Add(task); // Add task to task list
}
else if (host.HostType == "Machine2")
{// machine type 2
var task = CallRestartMachine2(host.IP);
tasks.Add(task); // Add task to task list
}
}
// Run all tasks in task list in parallel
await Task.WhenAll(tasks);
}
// ASYNC METHODS until here
private static async Task CallRestartMachine1(string host)
{// helper method: reboot machines of type 1
await Task.Run(() =>
{
RebootByWritingAFile(#"D:\RebootMe.bm", "reboot");
});
}
private static async Task CallRestartMachine2(string host)
{// helper method: reboot machines of type 2
await Task.Run(() =>
{
RebootByNetwork(host, "user", "pwd");
});
}
// STATIC METHODS here, going forward
private static void RebootByWritingAFile(string inPath, string inText)
{// This method does a lot of checks using more static methods, but then only writes a file
try
{
File.WriteAllText(inPath, inText); // static m
}
catch
{
// do nothing for now
}
}
private static void RebootByNetwork(string host, string user, string pass)
{
// Locals
string rawASIC = "";
SshClient SSHclient;
SshCommand SSHcmd;
// Send reboot command to linux machine
try
{
SSHclient = new SshClient(host, 22, user, pass);
SSHclient.Connect();
SSHcmd = SSHclient.RunCommand("exec /sbin/reboot");
rawASIC = SSHcmd.Result.ToString();
SSHclient.Disconnect();
SSHclient.Dispose();
}
catch
{
// do nothing for now
}
}
}
}
This setup calls my static methods asynchronously. I hope this helps someone who was stuck with a similar problem. Thanks again for all your input.

I think you should reconsider the "high overhead" of threads: This overhead is IMHO negligable when compared to the network roundtrips and waiting times on every RPC call. I am pretty sure, that the resources needed to create N threads is marginal against the resources needed to run N networked requests (be they http[s], RPC or whatever).
My approach would be to queue the tasks into a threadsafe collection (ConcurrentQueue, ConcurrentBag and friends), then spawn a finite number of threads looping through those work items until the collection is empty and just end.
This would not only allow you to run the tasks in parallel, it would allow you to run them in a controlled parallel way.

Related

In C#, how to know when external service is done making callbacks?

I have an external reference in my .NET console application that does some language translations on large strings for me.
In a loop, I make a bunch of calls to the service. There are probably 5000-8000 calls total.
The service requires that I implement a callback function so that it can give the translated string back to me when the work is completed. In another class which inherits the TranslationService's interface, I have implemented their callback function:
class MyTranslationServiceCallback : TranslationService.ITranslationServiceCallback
{
public void TranslateTextCallback(string sourceContent, string responseContent)
{
UpdateMyDatabase(responseContent);
}
}
When debugging, I have added Console.Readkey at the very end of my Main() to prevent the app from closing so that it can finish getting all of the callbacks. So far, I have just assumed that when it stops entering the callback function for a minute or so that it is "complete" (I know, this is bad).
So it looks like:
class Program
{
static void Main(string[] args)
{
foreach (var item in itemList)
{
TranslationService.TranslateText(item.EnglishText, "french");
}
Console.Readkey()
}
}
What is the proper way to determine whether or not all the callbacks have been completed?
Since translation service does not have any way of telling the status of translations you will need to keep track of the calls made and callbacks. Create a singleton which has a counter and increment with each call. Decrease the count in each call back.
Why not use the async framework built into .NET? All you need to do is to fire off tasks and keep track of them in an array, where then you can call Task.WhenAll to block the program until all Tasks are complete.
Note: I'm using the Nito.AsyncEx NuGet Package, in order to run async code from Console apps.
class Program
{
static int Main(string[] args)
{
return AsynContent.Run(() => MainAsync(args));
}
static async Task<int> MainAsync(string[] args)
{
var taskList = new List<Task>();
foreach (var item in itemList)
{
Task.Factory.StartNew(() => TranslationService.TranslateText(item.EnglishText, "french");
}
Task.WhenAll(taskList.ToArray());
}
}
If you're implementing this in .NET, then async/await is your friend.
It would be great if, rather than returning the result via callbacks, TranslationService returned a Task<string>.
Then you could implement the following:
static async Task TranslateAllItems(IEnumerable<Item> list)
{
foreach(var item in itemList)
{
string result = await TranslationService.TranslateText(item.EnglishText, "french");
UpdateMyDatabase(item.EnglishText, content);
}
}
static void Main(string[] args)
{
Task task = TranslateAllItems(itemList);
task.Wait();
Console.ReadKey();
}
The above solution would perform each translation in sequence, waiting for one translation task to complete before commencing with the next one.
If it would be faster to start all of the translations, then wait for the entire batch to finish:
static Task TranslateAllItems(IEnumerable<Item> list)
{
List<Task> waitingTasks = new List<Task>();
foreach(var item in itemList)
{
string englishText = item.EnglishText;
var task = TranslationService.TranslateText(englishText , "french")
.ContinueWith(taskResult => UpdateMyDatabase(englishText, taskResult.Result);
waitingTasks.Add(task);
}
return Task.WhenAll(waitingTasks);
}

Adding List of tasks without executing

I have a method which returns a task, which I want to call multiple times and wait for any 1 of them to be successful. The issue I am facing is as soon as I add the task to the List, it executes and since I added delay to simulate the work, it just block there.
Is there a way to add the the tasks to the list without really executing it and let whenAny execute the tasks.
The below code is from Linqpad editor.
async void Main()
{
var t = new Test();
List<Task<int>> tasks = new List<Task<int>>();
for( int i =0; i < 5; i++)
{
tasks.Add(t.Getdata());
}
var result = await Task.WhenAny(tasks);
result.Dump();
}
public class Test
{
public Task<int> Getdata()
{
"In Getdata method".Dump();
Task.Delay(90000).Wait();
return Task.FromResult(10);
}
}
Update :: Below one makes it clear, I was under the impression that if GetData makes a call to network it will get blocked during the time it actually completes.
async void Main()
{
OverNetwork t = new OverNetwork();
List<Task<string>> websitesContentTask = new List<Task<string>>();
websitesContentTask.Add(t.GetData("http://www.linqpad.net"));
websitesContentTask.Add(t.GetData("http://csharpindepth.com"));
websitesContentTask.Add(t.GetData("http://www.albahari.com/nutshell/"));
Task<string> completedTask = await Task.WhenAny(websitesContentTask);
string output = await completedTask;
Console.WriteLine(output);
}
public class OverNetwork
{
private HttpClient client = new HttpClient();
public Task<string> GetData(string uri)
{
return client.GetStringAsync(uri);
}
}
since I added delay to simulate the work, it just block there
Actually, your problem is that your code is calling Wait, which blocks the current thread until the delay is completed.
To properly use Task.Delay, you should use await:
public async Task<int> Getdata()
{
"In Getdata method".Dump();
await Task.Delay(90000);
return 10;
}
Is there a way to add the the tasks to the list without really executing it and let whenAny execute the tasks.
No. WhenAny never executes tasks. Ever.
It's possible to build a list of asynchronous delegates, i.e., a List<Func<Task>> and execute them later, but I don't think that's what you're really looking for.
There are multiple tasks in your Getdata method. First does delay, but you are returning finished task which returned 10. Try to change your code like this
return Task.Delay(90000).ContinueWith(t => 10)

C# .Net - How to make application wait until all threads created in Library are finished

I am trying to create a logging library and things are good until the application shutdown is called. When application shutdown is called, any unfinished thread is killed and the specific log is lost.
As of now application exits even before the first 10 threads are complete. I want help on how to make the application wait until all threads created by library are done.
NOTE:
Requirement I got are like this. Modifications should be only in the class 'Logging' since this will be a library and will be provided to end users. Handling of logging issues during app shutdown must be done within it. This is where I have trouble now.
Alternatively a solution like create an event in logging class to trigger all logging complete, and ask user to call app exit on that event is possible, but that I am trying to avoid since it adds that burden to end user and adds complexity for implementations. There is a possibility they may skip it, which I do not want. I am looking for a solution like user should do 'Logging.AddException(....)' and then forget about it.
Please help. Provide comments if you are not clear about the idea.
Here is the full code abstract which you can put into a console application.
Note: Look for comments in CASE 1 and CASE 2.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MultithreadKeepAlive
{
class Program
{
static void Main(string[] args)
{
LogLoadTest();
Logging.AddExceptionEntryAsync(new Exception("Last Exception"));
/*
* USE CASE 1: Enable the below lines and you will see how long it is supposed to take.
* Notice that currentDomain_ProcessExit will not trigger if below gets uncommented
*/
//Console.WriteLine("Main thread wait override");
//Console.ReadLine();
}
static void LogLoadTest()
{
//In real world this will be called from any place of application like startup or just after application shutdown is initiated.
//: NOTICE: Unlike the sample here, this will never be on loop and I am not looking for handling multithreads in this class.
// That responsibility I am planning to assign to Logging class.
// AND ALSO the class Logging is going to be in a seperate signed assembly where user of this class ('Program') should not worry about multithreads.
Task t;
for (int i = 0; i < 40; i++)
{
t = Logging.AddExceptionEntryAsync(new Exception("Hello Exception " + i), "Header info" + i);
}
}
}
public class Logging
{
static List<Task> tasks = new List<Task>();
static AppDomain currentDomain;
static Logging()
{
currentDomain = AppDomain.CurrentDomain;
currentDomain.ProcessExit += currentDomain_ProcessExit;
}
public static async Task AddExceptionEntryAsync(Exception ex, string header = "")
{
Task t = Task.Factory.StartNew(() => AddExceptionEntry(ex, header));
tasks.Add(t);
await t;
}
public static void AddExceptionEntry(Exception ex, string header)
{
/* Exception processing and write to file or DB. This might endup in file locks or
* network or any other cases where it will take delays from 1 sec to 5 minutes. */
Thread.Sleep(new Random().Next(1, 1000));
Console.WriteLine(ex.Message);
}
static void currentDomain_ProcessExit(object sender, EventArgs e)
{
Console.WriteLine("Application shutdown triggerd just now.");
Process.GetCurrentProcess().WaitForExit(); //1st attempt.
//Task.WaitAll(tasks.ToArray()); //2nd attempt
while (tasks.Any(t => !t.IsCompleted)) //3rd attempt.
{
}
/* USE CASE 2: IF WORKING GOOD, THIS WILL BE DISPLAYED IN CONSOLE AS LAST
* MESSAGE OF APPLICATION AND WILL WAIT FOR USER. THIS IS NOT WORKING NOW.*/
Console.WriteLine("All complete"); //this message should show up if this work properly
Console.ReadLine(); //for testing purpose wait for input from user after every thread is complete. Check all 40 threads are in console.
}
}
}
You can try
Task.WaitAll(tasks);
This waits for all of the provided Task objects to complete execution.
UPDATE : using async/await
With async and await, we formalize and clarify how asynchronous, non-blocking methods begin and end. An async method can return only void or a Task.
static void Main()
{
// Create task and start it.
// ... Wait for it to complete.
Task task = new Task(AsyncMethod);
task.Start();
task.Wait();
}
public static async void AsyncMethod(){
await AnotherMehod();}
static async Task AnotherMehod() { //TODO}
As of now I myself found a workaround.
/// <summary>
/// Makes the current thread Wait until any of the pending messages/Exceptions/Logs are completly written into respective sources.
/// Call this method before application is shutdown to make sure all logs are saved properly.
/// </summary>
public static void WaitForLogComplete()
{
Task.WaitAll(tasks.Values.ToArray());
}
Step 1: Consider changing to Task.Run() if you do not want the scheduler to be involved. I'm also assuming you want to wait until all async tasks finish.
public static AddExceptionEntry(Exception ex, string header = "")
{
Task t = Task.Factory.StartNew(() => AddExceptionEntry(ex, header));
tasks.Add(t);
WaitForExecutionAsync().ConfigureAwait(true);
}
public static async Task WaitForExecutionAsync()
{
if(tasks.Count >0)
await Task.WhenAll(tasks.ToArray());
// Raise Event.
}
To Block just call this to run sync vs async:
WaitForExecution().GetAwaiter().GetResult();

Async Task await

I am trying to understand async method in c#
I have created a wcf service that delay 10 seconds. I am calling the wcf service from a console application the value asyncValue is never sets. It just closes the console app.
Why is the asyncValue never set to Ok?
Wcf Service
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AsyncService
{
[OperationContract]
public async Task<string> TimeAsync()
{
await Task.Delay(10000);
return "Ok";
}
}
Console application
static void Main(string[] args)
{
Task<string> asyncValue = GetAsyncTimeFromWcf();
}
private static async Task<string> GetAsyncTimeFromWcf()
{
AsyncService.AsyncServiceClient client = new AsyncService.AsyncServiceClient();
Task<string> asyncTime = client.TimeAsync();
return await asyncTime;
}
For the await keyword to make sense you need to do something with the return value. The purpose of the async/await feature is to allow the application to continue with other, non-related, code until you need to use the result of the awaited task.
In your case, you call await but you don't use the return value, so the Main method continues and effectively exists the application. For example, try
static void Main(string[] args)
{
Task<string> asyncValue = GetAsyncTimeFromWcf();
Console.WriteLine("Waiting for server...");
Console.WriteLine(String.Format("Result = {0}", asyncValue.Result);
Console.ReadKey();
}
You should find the console will output Waiting for server... for 10 seconds then once the server returns it will display Ok.
asyncValue is a Task whose Result will contain the final value when it finishes. You have to wait for this result either by calling asyncValue.Result or by calling asyncValue.Wait() and then check asyncValue.Result.
You can't use await asyncValue or await GetAsyncTimeFromWcf() in the Main method of a Console program, because you can't mark it as async.
Just try:
Task<string> asyncValue = GetAsyncTimeFromWcf();
var time=asyncValue.Result;
Console.WriteLine(time);
Your main method ends before the WCF call returns.
To see the result, you can add the line:
Console.WriteLine(asyncValue.Result);
The call to Result, will Wait for the task to complete, and then get the result.

How to write simple async method?

Using latest CTP5 with async/await keywords, I wrote some code, which apparently cannot compile:
class Program
{
public class MyClass
{
async public Task<int> Test()
{
var result = await TaskEx.Run(() =>
{
Thread.Sleep(3000);
return 3;
});
return result;
}
}
static void Main(string[] args)
{
var myClass = new MyClass();
//The 'await' operator can only be used in a method or lambda marked with the 'async' modifier error ??!!
int result = await myClass.Test();
Console.ReadLine();
}
}
What is th reason of "The 'await' operator can only be used in a method or lambda marked with the 'async' modifier error?" (I've selected the line which Visual Studio point me to)
I don't know if you can mark Main as async, but you need to include the async keyword in the declaration of any method that uses await. For example:
public async void DoStuffAsync ()
{
var myClass = new MyClass ();
int result = await myClass.TestAsync ();
}
await is not the same as Wait(); doing an await is a significant re-writing of that method, and in particular affects the expectation of how that method exits to the caller. You are right in that it doesn't actually do much (caveat: return types) except tell the compiler to enable some things (as do switches like unsafe, checked and unchecked if you think about it) - but consider: this actually matters hugely in your example. If Main() exits (and we assume no other threads) - you exe is toast. Gone. No longer exists. Adding async makes you consider that just because the method exits doesn't mean it has finished. You really don't want Main() exiting before you are ready.
As a secondary effect, this switch also formalises that the method can only return things like Task; without the switch, you might be tempted to make it async later, which could be a significantly breaking change.
An async method can have a return type of void or Task. If the return type is not void the caller can still use the standard Wait mechanism introduced in .Net 4 inside the Main entry method (which can not be marked async). Here's a simple example:
static void Main(string[] args)
{
string address = "http://api.worldbank.org/countries?format=json";
Task t = LoadJsonAsync(address);
// do other work while loading
t.Wait();
Console.WriteLine("Hit ENTER to exit...");
Console.ReadLine();
}
private async static Task LoadJsonAsync(string address)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(address);
// Check that response was successful or throw exception
response.EnsureSuccessStatusCode();
// Read response asynchronously as JsonValue and write out top facts for each country
JsonArray readTask = await response.Content.ReadAsAsync<JsonArray>();
Console.WriteLine("First 50 countries listed by The World Bank...");
foreach (var country in readTask[1])
{
Console.WriteLine(" {0}, Capital: {1}",
country.Value["name"],
country.Value["capitalCity"]);
}
}

Categories

Resources