C# 如何异步调用静态方法
我试图构造一个简单的类,它根据要重新启动的机器类型调用重新启动函数。被调用的方法引用包含公共静态方法的库。我想使用Task异步调用这些静态方法,以便并行调用reboot方法。以下是迄今为止的代码: 编辑 根据社区的要求,这是同一问题的一个版本,下面的代码正在编译。请不要告诉我您需要lib,还需要在项目中设置对它的引用C# 如何异步调用静态方法,c#,asynchronous,task,C#,Asynchronous,Task,我试图构造一个简单的类,它根据要重新启动的机器类型调用重新启动函数。被调用的方法引用包含公共静态方法的库。我想使用Task异步调用这些静态方法,以便并行调用reboot方法。以下是迄今为止的代码: 编辑 根据社区的要求,这是同一问题的一个版本,下面的代码正在编译。请不要告诉我您需要lib,还需要在项目中设置对它的引用 // libs using System.IO; using System.Threading.Tasks; using System.Collections.Generic;
// 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
}
}
}
}
如果无法访问主机,则需要20秒执行。如果无法访问10台主机,则顺序持续时间为20*10=200秒
我知道一些看似相似的问题,例如
但是,引用的lambda表达式仍然给我留下了相同的编译器错误[此方法缺少等待运算符…]。另外,我不想产生显式线程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);
});
}
有没有办法包装静态方法调用,使所有静态子方法都不需要重写为异步
提前感谢大家。您的代码奇怪地混合了异步和连续,甚至无法编译。你需要让它一直异步。当你调用重启机器时。。。而且这个调用不能等待,您可以在该调用上安排继续,即重新启动计算机…ContinueWitht=>Console.WriteLine'All Done'
您的代码奇怪地混合了异步和连续,甚至无法编译。你需要让它一直异步。当你调用重启机器时。。。而且这个调用不能等待,您可以在该调用上安排继续,即重新启动计算机…ContinueWitht=>Console.WriteLine'All Done'
我认为您应该重新考虑线程的高开销:与网络往返和每次RPC调用的等待时间相比,这种开销是可以忽略的。我很确定,创建N个线程所需的资源与运行N个网络请求所需的资源相比是微不足道的,无论是http[s]、RPC还是其他什么 我的方法是将任务排队到线程安全集合ConcurrentQueue、ConcurrentBag和friends中,然后生成有限数量的线程,在这些工作项中循环,直到集合为空并刚刚结束
这不仅允许您并行运行任务,还允许您以受控的并行方式运行它们。我认为您应该重新考虑线程的高开销:与网络往返和每次RPC调用的等待时间相比,此开销是不可忽略的。我很确定,创建N个线程所需的资源与运行N个网络请求所需的资源相比是微不足道的,无论是http[s]、RPC还是其他什么 我的方法是将任务排队到线程安全集合ConcurrentQueue、ConcurrentBag和friends中,然后生成有限数量的线程,在这些工作项中循环,直到集合为空并刚刚结束
这不仅允许您并行运行任务,还允许您以受控的并行方式运行任务。各位,感谢您的输入和帮助。在过去的几天里,我一直在研究他的方法,并提出了以下异步调用静态方法的方法:
// 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
}
}
}
}
此安装程序异步调用我的静态方法。我希望这能帮助那些遇到类似问题的人。再次感谢您的投入。各位,感谢您的投入和帮助。在过去的几天里,我一直在研究他的方法,并提出了以下异步调用静态方法的方法:
// 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
}
}
}
}
此安装程序异步调用我的静态方法。我希望这能帮助那些遇到类似问题的人。再次感谢您的所有输入。如何并行运行多个方法?通过在不同的线程中运行它们。你不喜欢线吗?重新编写异步方法并等待它们。此代码甚至不会编译,因此我们很难帮助您,因为这显然不是您的代码外观like@Dave,代码现在在安装和引用库后即可编译。如何并行运行多个方法?通过在不同的线程中运行它们
s你不喜欢线吗?重新编写异步方法并等待它们。此代码甚至不会编译,因此我们很难帮助您,因为这显然不是您的代码外观like@Dave,代码现在会在安装和引用库后编译。回答得好。我要补充的唯一一点是,现在这些方法是异步的,或者您已经创建了其他异步方法,通常最好使用异步后缀重命名它们。谢谢大家的意见。我编辑了我的问题,以显示除了简单的代码构造之外我还尝试了什么。实际上,如果每次调用都在一个新线程上调用这些方法,我最终会执行准异步执行,但会有显式的线程切换开销。@JohanP,一旦安装并引用Renci.SHH lib,代码现在就可以编译了。回答得好。我要补充的唯一一点是,现在这些方法是异步的,或者您已经创建了其他异步方法,通常最好使用异步后缀重命名它们。谢谢大家的意见。我编辑了我的问题,以显示除了简单的代码构造之外我还尝试了什么。本质上,如果我每次调用都在一个新线程上调用这些方法,我最终会得到一个准异步执行,但会有显式的线程切换开销。@JohanP,一旦安装并引用Renci.SHH lib,代码现在就可以编译了。这个开销是IMHO negigable-OP谈论的是新线程,而不是线程池线程。产生显式线程有一个可测量的成本,大约。此外,无论您使用显式线程还是线程池线程,这些线程都没有做任何有用的事情。因此,除了开销事实上不可忽略之外,他们仍然没有完成任何事情。@Eugen Rieck,谢谢你的投入。我将使用这种方法作为最后手段。但是,一般指南建议不要在每个CPU核心上使用超过50个线程,因为这样会积累太多的线程切换开销。我有另一种方法,我在不到5秒钟的时间内扫描本地服务器网络上的数万个IP地址,全部在一个异步调用中完成。这就是我试图实现的,但是调用lib静态方法仍然只能强制执行同步。另一个很好的区别是,为什么我不应该使用多线程,而应该使用多任务,这很好地解释了@MickyD 1MB/线程是可忽略的,如果您将其与通过SMB进行RPC调用所需的12MB进行比较,那么这个开销就是IMHO NERGLIGABLE-OP所说的新线程,而不是线程池线程。产生显式线程有一个可测量的成本,大约。此外,无论您使用显式线程还是线程池线程,这些线程都没有做任何有用的事情。因此,除了开销事实上不可忽略之外,他们仍然没有完成任何事情。@Eugen Rieck,谢谢你的投入。我将使用这种方法作为最后手段。但是,一般指南建议不要在每个CPU核心上使用超过50个线程,因为这样会积累太多的线程切换开销。我有另一种方法,我在不到5秒钟的时间内扫描本地服务器网络上的数万个IP地址,全部在一个异步调用中完成。这就是我要实现的,但是调用lib静态方法只会强制同步执行。另外一个很好的区别是,如果您将它与通过SMB进行RPC调用所需的12MB进行比较,那么解释了为什么我不应该使用多个线程,而是多任务处理的另一个好区别。
// 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
}
}
}
}