[英]How to Asynchronously Call Static Methods
我正在嘗試構造一個簡單的類,該類根據要重新啟動的計算機類型調用重新啟動功能。 被調用的方法是指包含公共靜態方法的庫。 我想使用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秒。
我知道一些看似相似的問題,例如
但是,引用的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);
});
}
有沒有辦法包裝靜態方法調用,使所有靜態子方法都不需要全部重寫為異步方法?
謝謝大家。
您的代碼將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);
}
大家,謝謝您的投入和幫助。 在過去的幾天里,我一直在和他玩耍,並提出了以下方法來異步調用靜態方法:
// 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
}
}
}
}
此設置異步調用我的靜態方法。 我希望這對遇到類似問題的人有所幫助。 再次感謝您的所有意見。
我認為您應該重新考慮線程的“高開銷”:與每個RPC調用的網絡往返次數和等待時間相比,IMHO的開銷可以忽略不計。 我非常確定,創建N個線程所需的資源與運行N個網絡請求(無論是http [s],RPC還是其他)所需的資源相比是微不足道的。
我的方法是將任務排入一個線程安全的集合( ConcurrentQueue
, ConcurrentBag
和好友),然后生成有限數量的線程循環通過這些工作項,直到該集合為空並結束。
這不僅可以讓您並行運行任務,還可以以受控的並行方式運行它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.