我在Task
内更新用户界面时遇到了一个奇怪的问题。下面的代码取自我的GetWorkingProxies
方法,该方法基本上需要许多代理,ping它们并返回工作列表。
我正在使用Task.WhenAll
创建并发性,因此它一次尽可能多地ping,而不是一次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更新如何工作,而不是同一个Action委托中的另一个?
为什么会这样?
编辑:
实际例外是:
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 ...
答案 0 :(得分:2)
我会做如下的最小改动。注意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);