从任务/动作委托中更新UI的奇怪行为

本文关键字:UI 更新 任务 | 更新日期: 2023-09-27 18:04:03

我正面临着一个奇怪的问题,即从Task内部更新UI。下面的代码取自我的GetWorkingProxies方法,它基本上接受许多代理,ping它们,并返回工作列表。

我使用Task.WhenAll来创建并发,所以它一次ping尽可能多的,而不是一次一个。

问题是,在Action委托中我更新了UI。

Action<string> checkProxy = s => {
    Ping ping = new Ping();
    try {
        string[] proxy = s.Split(':');
        lblLog.Text = "Testing Proxy: " + proxy[0];
        PingReply reply = ping.Send(proxy[0], Convert.ToInt32(proxy[1]));
        if(reply.Status == IPStatus.Success)
        {
             workingProxies.Add(s);
             lblSuccessProxies.Text = workingProxies.Count.ToString();
        }
        else
        {
             failedProxies.Add(s);
             lblFailedProxies.Text = failedProxies.Count.ToString();
        }
    } catch(Exception ex)
    {
        // DEBUG
        Console.WriteLine(ex);
    }
};

下面是创建Task数组的代码…

Task[] tasks = new Task[proxies.Count];
for (int i = 0; i < proxies.Count; i++)
{
    string tmp = proxies[i];
    tasks[i] = Task.Run(() => checkProxy(tmp));
}
await Task.WhenAll(tasks);

我不能理解的是为什么lblLog.Text = "Testing Proxy: " + proxy[0];工作得很好,但lblFailedProxies.Text = failedProxies.Count.ToString();lblSuccessProxies.Text = workingProxies.Count.ToString();都抛出了System.InvalidOperationException

我知道从测试,这是一个跨线程的问题,但如何能一个UI更新工作,而不是另一个从同一个动作委托?

为什么会这样?

编辑:

实际的例外是:

A first chance exception of type 'System.InvalidOperationException' occurred in    System.Windows.Forms.dll
System.InvalidOperationException: Cross-thread operation not valid: Control 'lblSuccessProxies' accessed from a thread other than the thread it was created on.
   at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.Control.set_WindowText(String value)
   at System.Windows.Forms.Control.set_Text(String value)
   at System.Windows.Forms.Label.set_Text(String value)
   at SoundCloudPlays.Form1.<>c__DisplayClass12.<getProxies>b__10(String s) in ...

从任务/动作委托中更新UI的奇怪行为

我会像下面这样做一个最小的改变。注意await SendAsync()的使用,现在延续在UI线程上异步发生,所以访问UI是安全的。此外,您不再需要使用Task.Run:

Func<string, Task> checkProxyAsync = async(s) => {
    Ping ping = new Ping();
    try {
        string[] proxy = s.Split(':');
        lblLog.Text = "Testing Proxy: " + proxy[0];
        PingReply reply = await ping.SendAsync(proxy[0], Convert.ToInt32(proxy[1]));
        if(reply.Status == IPStatus.Success)
        {
             workingProxies.Add(s);
             lblSuccessProxies.Text = workingProxies.Count.ToString();
        }
        else
        {
             failedProxies.Add(s);
             lblFailedProxies.Text = failedProxies.Count.ToString();
        }
    } catch(Exception ex)
    {
        // DEBUG
        Console.WriteLine(ex);
    }
};

使用:

Task[] tasks = new Task[proxies.Count];
for (int i = 0; i < proxies.Count; i++)
{
    string tmp = proxies[i];
    tasks[i] = checkProxyAsync(tmp);
}
await Task.WhenAll(tasks);