Блокировка UI несмотря на асинхронность

205
29 марта 2018, 06:13

Создаю и запускаю задачу вот так:

var task = new Task(WorkWithResult, cts.Token); //cts.Token - маркер отмены, но это не важно
task.Start();

Переменные, которые используются в WorkWithResult:

var    result             = new int[N];
string stringResult = string.Empty;

Жду завершения задачи стандартно, вот так:

await task.ConfigureAwait(true);

WorkWithResult - локальная функция, выглядит вот так:

void WorkWithResult()
        {
            var sbResult = new StringBuilder(N * 5);
            var progressValue = 0.0;
            var progressStep  = 100d / N / 2;
            result[0] = PrimeNumbers.Next_prime(0);
            progress.Report((int) (progressValue += progressStep));
            for (var i = 0; i < result.Length - 1; i++)
            {
                result[i + 1] = PrimeNumbers.Next_prime(result[i]);
                cts.Token
                   .ThrowIfCancellationRequested();
                progress.Report((int) (progressValue += progressStep));
            }
            foreach (var number in result)
            { // Блокировка UI. Почему?
                sbResult.AppendLine(number.ToString()); 
                cts.Token
                   .ThrowIfCancellationRequested(); 
                progress.Report((int) (progressValue += progressStep));
            }
            stringResult = sbResult.ToString();
        }

Во время выполнения последнего цикла происходит блокировка UI(массив чисел из 100 000, блокировка на 1 секунду). Почему? Код явно выполняется в другом потоке, я же запускаю его через Task. Все, чем отличается этот цикл от цикла 1 - это sbResult.AppendLine. Я подумал, что, возможно, дело в обращениях к массиву result, который создан не в том потоке, что выполняется WorkWithResult, но ведь блокировки не происходит в цикле 1.

Почему происходит блокировка, как ее избежать?

Answer 1

В первом цикле вы отправляете отчеты с некоторой задержкой между ними (задержка обусловлена сложностью поиска простого числа). Вот в этот промежуток UI и успевает прорисоваться, отреагировать на действия пользователя и т.п.

Во втором цикле вы молотите новые репорты без остановки, ведь AppendLine работает очень быстро! (Даже быстрее чем отправка отчета) А потому поток UI занят исключительно обработкой отчетов и не успевает сделать ничего кроме обработки отчетов.

Из возможных исправлений самый простой способ - это убрать progress.Report из второго цикла.

Альтернативный вариант - отправлять каждый (N/50)й отчет, а остальные пропускать (если нужна точность до десятых долей процента - то каждый (N/500)й).

Еще один вариант - отслеживать время работы и отправлять отчет каждые полсекунды.

READ ALSO
Обновление UI у клиента

Обновление UI у клиента

При попадании по игроку нужно обновлять показания его здоровья на его сторонеПочему этого не происходит и как правильно это реализовать?

210
Как получить номер недели?

Как получить номер недели?

Нужно получить номер неделиС первой неделей года всё понятно

195
Триггер на добавление в List&lt;T&gt;

Триггер на добавление в List<T>

В общем, хочу что бы перед добавление записи в коллекцию у меня выполнялось некоторое действие

198
Понимание шифрования данных .net

Понимание шифрования данных .net

Хочу уточнить пару вещей

202