Я пытаюсь создать простой класс, который вызывает функцию перезагрузки в зависимости от типа машины, которая будет перезагружена. Вызываемые методы ссылаются на библиотеку, которая содержит общедоступные статические методы. Я хочу асинхронно вызывать эти статические методы, используя Task для параллельного вызова методов перезагрузки. Вот код на данный момент:
РЕДАКТИРОВАТЬ После запроса сообщества, это теперь версия того же вопроса, с кодом ниже компиляции. Обратите внимание, что вам нужна библиотека Renci.SshNet, а также необходимо установить ссылки на нее в вашем проекте.
// 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 секунд.
Мне известны некоторые, казалось бы, похожие вопросы, такие как
Однако приведенные лямбда-выражения по-прежнему оставляют меня с той же ошибкой компилятора ["В этих методах отсутствуют операторы ожидания..."]. Кроме того, я не хочу порождать явные потоки (new Thread (() =>...)) из-за высоких издержек при перезапуске большого количества компьютеров в кластере.
Мне может понадобиться перезагрузить большое количество машин в кластере. Отсюда мой вопрос: как я могу изменить свою конструкцию, чтобы иметь возможность вызывать вышеупомянутые статические методы параллельно?
РЕДАКТИРОВАТЬ Благодаря комментариям @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);
});
}
Есть ли способ обернуть вызов статического метода так, что не нужно переписывать все статические дочерние методы как асинхронные?
Спасибо всем заранее.
Всем спасибо за ваш вклад и вашу помощь. Я поиграл с ним за последние пару дней и нашел следующий способ асинхронного вызова статического метода:
// 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
}
}
}
}
Эта установка вызывает мои статические методы асинхронно. Я надеюсь, что это помогает кому-то, кто застрял с подобной проблемой. Еще раз спасибо за ваш вклад.
Ваш код имеет странную смесь async
с продолжениями, и он даже не скомпилируется. Вы должны сделать это async
полностью. Когда вы вызываете RebootMachines(...)
и этот вызов не может быть await
, вы можете запланировать продолжения для этого, то есть 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);
}
Async
. docs.microsoft.com/en-us/dotnet/csharp/programming-guide/...
Я думаю, что вам следует пересмотреть "высокую нагрузку" на потоки: эти издержки ИМХО пренебрежимо малы по сравнению с обходами сети и временем ожидания при каждом вызове RPC. Я совершенно уверен, что ресурсы, необходимые для создания N потоков, незначительны по сравнению с ресурсами, необходимыми для выполнения N сетевых запросов (будь то http [s], RPC или что-то еще).
Мой подход заключался бы в том, чтобы поставить задачи в очередь в потокобезопасную коллекцию (ConcurrentQueue
, ConcurrentBag
и друзья), а затем создать конечное число потоков, проходящих по этим рабочим элементам, пока коллекция не станет пустой и не завершится.
Это не только позволяет запускать задачи параллельно, что позволит запускать их в управляемом параллельно.
new Thread
потоке, а не о потоке пула потоков. Существует измеримая стоимость порождения явных потоков, около 1 МБ на поток .
async
и ожидайте их.