简体   繁体   中英

How to Asynchronously Call Static Methods

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

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 await ed, you can schedule continuations on that ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM