我正在尝试构造一个简单的类,该类根据要重新启动的计算机类型调用重新启动功能。被调用的方法是指包含公共静态方法的库。我想使用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秒。
我知道一些看似相似的问题,例如
但是,引用的lambda表达式仍然给我带来相同的编译器错误[“此方法缺少await运算符...”]。另外,如果重启群集中的大量计算机,由于高开销,我也不想产生显式线程(新Thread(()=> ...))。
我可能需要重新引导群集中的大量计算机。因此,我的问题是:如何更改构造以能够并行调用上述静态方法?
编辑 感谢@JohanP和@MickyD的评论,我想详细说明一下我实际上已经尝试编写两种静态方法的异步版本。但是,这使我陷入困境,每次在async方法中调用静态方法时,我都会收到编译器警告,该调用将是同步的。这是一个示例,说明了我如何将对方法的调用包装为异步任务,希望以异步方式调用相关方法。
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);
});
}
是否可以包装静态方法调用,以使所有静态子方法都不需要全部重写为异步方法?
谢谢大家。
答案 0 :(得分:1)
您的代码将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);
}
答案 1 :(得分:0)
所有人,谢谢您的投入和帮助。在过去的几天里,我一直在和他玩耍,并提出了以下方法来异步调用静态方法:
// 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
}
}
}
}
此设置异步调用我的静态方法。我希望这对遇到类似问题的人有所帮助。再次感谢您的所有输入。
答案 2 :(得分:-2)
我认为您应该重新考虑线程的“高开销”:与每个RPC调用的网络往返次数和等待时间相比,IMHO的开销可以忽略不计。我非常确定,创建N个线程所需的资源与运行N个网络请求(无论是http [s],RPC还是其他)所需的资源相比是微不足道的。
我的方法是将任务排队到线程安全的集合(ConcurrentQueue
,ConcurrentBag
和朋友)中,然后生成有限数量的线程循环通过这些工作项,直到集合为空且仅结束。
这不仅使您可以并行运行任务,而且还可以以受控并行方式运行它们。