启动时的C#WPF异步



我尝试构建一个简单的WPF应用程序,并在MainWindow中有一个listbox。背景是它对一些机器进行ping,但我希望GUI已经打开,并且每当ping完成时,listbox都应该更新内容。

让我展示一下我的代码。主窗口.xaml.cs

public ObservableCollection<String> LbStatus
{
get { return BL.GetStatus(); }
}

BL.cs

public static ObservableCollection<string> GetStatus()
{
List<string> hostnames = new List<String>(GetHostnames());
ObservableCollection<string> status = new ObservableCollection<string>();

foreach(string hostname in hostnames)
{
Task<string> task = Task.Run(async () => await 
PingHost(HM.GetHostname(hostname)));
status.Add(task.Result); 
}
return status; 
}
static public async Task<string> PingHost(string hostname)
{
Ping x = new Ping();
try
{
PingReply reply = await x.SendPingAsync(hostname, 5000);
if (reply.Status == IPStatus.Success)
{
return "Online";
}
else
return "Offline";
}
catch (Exception excep)
{
//exception handling
}
}

我认为问题是我的程序在等待task.result。但在我的脑海中,它应该跳过并不返回任何内容,无论何时完成,它都应该返回正确的字符串。但是(这就是我这么做的全部原因(GUI应该已经启动了。

我希望我的问题有意义,有人能帮我:-(

谢谢!

我建议您引入一些更改
首先,LbStatus属性的实现非常可疑
在这样的实现中,每次访问此属性时,都会创建一个新的集合,这可能会导致:它们的状态不同;同时调用CCD_ 6方法若干次。并且存在与多个集合实例的存在相关联的许多其他问题;认为";这是同一个例子
通过向您提供此属性的实现:

private ObservableCollection<string> _lbStatus;
public ObservableCollection<string> LbStatus
{
get 
{
if (_lbStatus == null)
_lbStatus = BL.GetStatus();
return _lbStatus;
}
}

第二个问题,在方法BL.GetStatus()中,您需要返回集合,而无需等待其完成
因此,用于填充它的循环应该在一个单独的异步方法中
BL.GetStatus()方法应该调用此异步方法,但不要等待它完成。

我建议你这样做:

public static ObservableCollection<string> GetStatus()
{
ObservableCollection<string> status = new ObservableCollection<string>();
FillStatus(status);
return status;
}
public static async Task FillStatus(ICollection<string> status)
{
foreach (string hostname in GetHostnames())
{
status.Add(await PingHost(HM.GetHostname(hostname)));
}
}

实现属性的另一个选项,考虑将方法分为两部分
它将只使用异步部分。

private ObservableCollection<string> _lbStatus;
public ObservableCollection<string> LbStatus
{
get 
{
if (_lbStatus == null)
{
_lbStatus = new ObservableCollection<string>();
BL.FillStatus(_lbStatus);
}
return _lbStatus;
}
}

考虑到集合在UI元素中的使用,其更改应该在主UI线程中进行。要执行此操作,必须将Dispatcher传递给该方法。

public static async Task FillStatus(ICollection<string> status, Dispatcher dispatcher)
{
foreach (string hostname in GetHostnames())
{
string item = await PingHost(HM.GetHostname(hostname));
dispatcher.BeginInvoke(new Action(() => status.Add(item)))
}
}
private ObservableCollection<string> _lbStatus;
public ObservableCollection<string> LbStatus
{
get 
{
if (_lbStatus == null)
{
_lbStatus = new ObservableCollection<string>();
BL.FillStatus(_lbStatus, Dispatcher);
}
return _lbStatus;
}
}

您的问题与属性getter有关。首先,在继续之前,这是一个很好的背景问题:你必须把Task放进去吗。在方法中运行以使其异步?

有了这个引用,您的方法PingHost被正确地编写为等待,但它是async并不一定意味着它不会阻塞调用线程这与您如何通过属性getter调用它有关——这是同步完成的您从同步方法GetStatus调用它,然后通过属性getter访问该同步方法。因此,尽管调用了async方法PingHost,但它没有被等待,因此阻塞了调用线程。

编辑说明:即使编写此命令是为了通过不同的调用正确等待,属性getter也会同步访问,因此以下解释仍然适用

这带来了第二个要读取的引用,它与异步getter有关。我认为这个答案真的很感人,尤其是答案中的第二项:

一个可以在数据绑定中使用但必须异步计算/检索的值。在这种情况下,对包含的对象使用异步工厂方法,或者使用异步InitAsync((方法。在计算/检索该值之前,数据绑定值将为默认值(T(。

这个人写了一篇非常好的博客文章,解释了如何实现这一点,我鼓励你去看看:https://blog.stephencleary.com/2013/01/async-oop-3-properties.html

最新更新