Обновление пользовательского интерфейса странного поведения из делегата задачи / действия

1

Я столкнулся с необычной проблемой с обновлением пользовательского интерфейса из Task. Код, приведенный ниже, берется из моего метода GetWorkingProxies который в основном принимает несколько прокси, пинги их и возвращает рабочий список.

Я использую Task.WhenAll для создания параллелизма, поэтому он пингует как можно больше за один раз, а не по одному за раз.

Проблема заключается в том, что в делегате Action я обновляю пользовательский интерфейс. Что ниже:

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 ...
Теги:
async-await
task-parallel-library

1 ответ

2
Лучший ответ

Я бы сделал минимальное изменение, как показано ниже. Обратите внимание на использование await SendAsync(), теперь продолжения происходят асинхронно в потоке пользовательского интерфейса, поэтому он безопасен для доступа к пользовательскому интерфейсу. Кроме того, вам больше не нужно использовать 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);
  • 0
    Это решение полностью блокирует пользовательский интерфейс.
  • 0
    @JamesJeffery, вы используете DNS-имена или IP-адреса? Возможно, что часть разрешения DNS-имен SendAsync блокируется. Если это так, попробуйте изменить его следующим образом: PingReply reply = await Task.Run(() => ping.Send(proxy[0], Convert.ToInt32(proxy[1])));
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню