我正在创建用于控制公司网络上的远程工作站和服务器的Form Aplication,并且必须创建“等待远程计算机重新启动”功能。这个功能还可以,但是我需要它异步运行,这是我的问题... 该功能首先检查联机/脱机状态以确定重新启动,然后检查远程计算机的新LastBootUpTime值,以确保它确实重新启动,而不仅仅是网络问题。 当我异步运行此检查时,如果使用.Get()方法,则ManagementObjectSearcher会触发死锁。当我改用PerformanceCounter时遇到同样的问题。
有3个主要对象: 1)表格课 2)关系类(由Form拥有) 3)RestartChecker类(由Relation拥有)
当RestartChecker获取进行重启的信息时,将此信息谷事件发送给Relation。关系使用它自己的事件将其发送到UI上的“表单”和“表单更改”图标。
这是我来自RestartChecker的代码(重要部分):
此方法在Relation类中,它启动RestartChecker。从Form类中调用此Relation方法。
public void StartRestartMonitoring()
{
restartChecker = new RestartChecker(machine.Name, machine.OperatingSystem.lastBootUpTime.Value, wmiSuccess);
//WasRestarted property calls event on value change to true. That event change icons on Form
restartChecker.RestartWasMade += new Action(() => { WasRestarted = true; });
restartChecker.Start();
}
此方法启动检查重启功能
Task checker;
CancellationTokenSource tokenSource;
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
checker = CheckActionAsync(token);
running = true;
}
这是更重要的部分=> Task方法应该异步运行
private async Task CheckActionAsync(CancellationToken ct)
{
bool isOnline = await RemoteTask.PingAsync(target, PING_TIMEOUT_SECONDS);
int onlineState = (isOnline) ? 0 : 1;
try
{
lastKnownBootUpTime = (isOnline) ? (GetLastBootUpTime(target, useWMI) ?? lastKnownBootUpTime) : lastKnownBootUpTime;
}
catch (Exception ex)
{
//Logs to File
EventNotifier.Log(ex,....);
}
//This part looks OK...
while (onlineState < 2)
{
if (ct.IsCancellationRequested) { return; }
bool actualOnlineState = await RemoteTask.PingAsync(target, PING_TIMEOUT_SECONDS);
onlineState += (actualOnlineState == isOnline) ? 0 : 1;
await Task.Delay(CHECK_INTERVAL);
}
while (!ct.IsCancellationRequested)
{
if (ct.IsCancellationRequested) { return; }
//Here, until I get properly value for LastBootUpTime of remote machine, I'm still trying again and again (beacause first try is cannot be OK => machine is Online, but services for WMI is not ready yet, so there is exception on first try)
while (newBootUpTime == null)
{
try
{
newBootUpTime = GetLastBootUpTime(target, useWMI);
}
catch (Exception ex)
{
//Some reactions to exception including logging to File
}
await Task.Delay(INTERVAL);
}
//This part looks ok too..
newBootUpTime = newBootUpTime.Value.AddTicks(-newBootUpTime.Value.Ticks % TimeSpan.TicksPerSecond);
lastKnownBootUpTime = lastKnownBootUpTime.Value.AddTicks(-lastKnownBootUpTime.Value.Ticks % TimeSpan.TicksPerSecond);
if (newBootUpTime.Value > lastKnownBootUpTime.Value)
{
RestartWasMade?.Invoke();
return;
}
await Task.Delay(CHECK_INTERVAL);
}
}
GetLastBoostUpTime方法
private static DateTime? GetLastBootUpTime(string target, bool useWMI)
{
DateTime? lastBootUpTime = null;
if (useWMI)
{
//wmiBootUpTime is SelectQuery
string dateInString = RemoteTask.SelectStringsFromWMI(wmiBootUpTime, new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", target))).First()[wmiBootUpTime.SelectedProperties[0].ToString()];
lastBootUpTime = (string.IsNullOrEmpty(dateInString)) ? null : (DateTime?)ManagementDateTimeConverter.ToDateTime(dateInString);
}
else
{
TimeSpan? osRunningTime = RemoteTask.GetUpTime(target);
lastBootUpTime = (osRunningTime == null) ? null : (DateTime?)DateTime.Now.Subtract(osRunningTime.Value);
}
return lastBootUpTime;
}
用于获取数据的WMI方法:
public static List<Dictionary<string, string>> SelectStringsFromWMI(SelectQuery select, ManagementScope wmiScope)
{
List<Dictionary<string, string>> result = new List<Dictionary<string, string>>();
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiScope, select))
{
//This line is deadlock-maker... Because remote machine services is not ready yet, searcher.Get() is trying
//until reach it's timeout (by default it is 30s) and that's my deadlock. For the time of running searcher.Get()
//there is 30s deadlock. Where is the mistake I've made? I supposed that this can not confront my UI thread
using (ManagementObjectCollection objectCollection = searcher.Get())
{
foreach (ManagementObject managementObject in objectCollection)
{
result.Add(new Dictionary<string, string>());
foreach (PropertyData property in managementObject.Properties)
{
result.Last().Add(property.Name, property.Value?.ToString());
}
}
return result;
}
}
}
用于获取数据的PerformanceCounte方法:
public static TimeSpan? GetUpTime(string remoteMachine = null)
{
try
{
using (PerformanceCounter upTime = (string.IsNullOrWhiteSpace(remoteMachine))
? new PerformanceCounter("System", "System Up Time")
: new PerformanceCounter("System", "System Up Time", null, remoteMachine))
{
upTime.NextValue();
return TimeSpan.FromSeconds(upTime.NextValue());
}
}
catch
{
return null;
}
}
异步ping方法
public async static Task<bool> PingAsync(string target, int pingTimeOut)
{
bool result = false;
Exception error = null;
using (Ping pinger = new Ping())
{
try
{
PingReply replay = await pinger.SendPingAsync(target, pingTimeOut * 1000);
result = (replay.Status == IPStatus.Success) ? true : false;
}
catch (Exception ex)
{
error = ex;
}
}
if (error != null) { throw error; }
return result;
}
答案 0 :(得分:2)
我在这里看不到死锁,但我看到您通过同步调用阻止了异步方法
newBootUpTime = GetLastBootUpTime(target, useWMI);
您可以在异步线程中异步调用此方法,或者使GetLastBootUpTime方法异步
newBootUpTime = await Task.Run(() => GetLastBootUpTime(target, useWMI));
您还应该使用上述方法,从异步方法中删除所有其他同步阻止调用。
仅当您致电
时,死锁才可能导致 checker.Wait();
在您创建Task checker
的线程中的某个位置(可能是UI线程)
您正在这样做吗?
您还可以在此处了解什么是死锁以及如何避免死锁
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html