Как асинхронно вызывать статические методы - PullRequest
0 голосов
/ 26 октября 2018

Я пытаюсь создать простой класс, который вызывает функцию перезагрузки в зависимости от типа машины, которая будет перезагружена.Вызываемые методы ссылаются на библиотеку, которая содержит общедоступные статические методы.Я хочу асинхронно вызывать эти статические методы, используя Task для параллельного вызова методов перезагрузки.Вот код на данный момент:

РЕДАКТИРОВАТЬ По запросу сообщества, это теперь версия того же вопроса, с кодом ниже компиляции.Обратите внимание, что вам нужна Renci.SshNet lib, а также необходимо установить ссылки на нее в вашем проекте.

// 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
            }
        }




    }
}

Моя единственная проблема с этой настройкой на данный момент заключается в том, что статическийметоды вызываются немедленно (последовательно) и не назначаются задаче.Например, строка

        ...
        else if (host.HostType == "Machine2")
        {// machine type 2
            var task = CallRestartMachine2(host.IP);
            tasks.Add(task);    // Add task to task list
        }  
        ...

выполняется в течение 20 секунд, если хост недоступен.Если 10 хостов недоступны, последовательная продолжительность составляет 20 * 10 = 200 секунд.

Мне известны некоторые, казалось бы, похожие вопросы, такие как

Однако приведенные лямбда-выражения по-прежнему оставляют меня с той же ошибкой компилятора [«В этих методах отсутствуют операторы ожидания ...»].Кроме того, я не хочу порождать явные потоки (новый Thread (() => ...)) из-за высоких издержек при перезапуске большого количества компьютеров в кластере.

Возможно, мне потребуется перезагрузитьбольшое количество машин в кластере.Отсюда мой вопрос: Как я могу изменить свою конструкцию, чтобы иметь возможность параллельно вызывать вышеупомянутые статические методы ?

EDIT Благодаря комментариям @JohanPи @MickyD, я хотел бы пояснить, что я на самом деле пытался написать асинхронную версию обоих статических методов.Тем не менее, это приводит меня в кроличью нору, где каждый раз, когда статический метод вызывается в асинхронном методе, я получаю предупреждение компилятора о том, что вызов будет синхронным.Вот пример того, как я пытался обернуть вызов метода как асинхронную задачу, надеясь вызвать зависимые методы асинхронным способом.

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);
    });

}

Есть ли способ обернуть вызов статического метода, такойчто все статические дочерние методы не нужно переписывать как асинхронные?

Заранее всем спасибо.

Ответы [ 3 ]

0 голосов
/ 26 октября 2018

Я думаю, что вам следует пересмотреть «высокую нагрузку» на потоки: эти издержки ИМХО пренебрежимо малы по сравнению с обходами сети и временем ожидания при каждом вызове RPC.Я почти уверен, что ресурсы, необходимые для создания N потоков, незначительны по сравнению с ресурсами, необходимыми для выполнения N сетевых запросов (будь то http [s], RPC или что-то еще).

Мой подход заключается в постановке в очередьзадач в потокобезопасную коллекцию (ConcurrentQueue, ConcurrentBag и друзья), затем создайте конечное число потоков, проходящих по этим рабочим элементам, пока коллекция не станет пустой и просто завершится.

Это не только позволит вамчтобы выполнять задачи параллельно, это позволило бы вам запускать их контролируемым параллельным способом.

0 голосов
/ 29 октября 2018

Всем, спасибо за ваш вклад и вашу помощь.Я играл с ним за последние пару дней и нашел следующий способ асинхронного вызова статического метода:

// 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
            }
        }




    }
}

Эта установка вызывает мои статические методы асинхронно.Я надеюсь, что это помогает кому-то, кто застрял с подобной проблемой.Еще раз спасибо за ваш вклад.

0 голосов
/ 26 октября 2018

Ваш код имеет странное сочетание async с продолжениями, и он даже не скомпилируется.Вы должны сделать это async полностью.Когда вы звоните RebootMachines(...), и этот вызов не может быть await ed, вы можете запланировать продолжения на этом, то есть 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);

}
...