Как бороться с синхронизацией (блокировкой) вызовов, чтобы пользовательский интерфейс не отвечал - PullRequest
0 голосов
/ 07 февраля 2019

Учитывая этот фрагмент кода, я заметил, что мой интерфейс некоторое время блокировался (Windows даже выдавала сообщение о том, что приложение не отвечает.

using (var zip = await downloader.DownloadAsZipArchive(downloadUrl))
{
    var temp = FileUtils.GetTempDirectoryName();
    zip.ExtractToDirectory(temp);   // BLOCKING CALL

    if (Directory.Exists(folderPath))
    {
        Directory.Delete(folderPath, true);
    }

    var firstChild = Path.Combine(temp, folderName);
    Directory.Move(firstChild, folderPath);
    Directory.Delete(temp);
}

После некоторых проверок я обнаружил, чтострока, которая гласит:

zip.ExtractToDirectory(temp);

- виновник.

Я думал, что превращения в него будет достаточно, чтобы заставить его работать:

await Task.Run(() => zip.ExtractToDirectory(temp));

Но… этоэто хорошее решение этой проблемы?

У меня есть опыт работы с System.Reactive (я полностью знаком с реактивным программированием), и я хотел бы знать, есть ли более элегантный способ справиться с этим.

Ответы [ 3 ]

0 голосов
/ 07 февраля 2019

Скорее всего, я бы реорганизовал код извлечения zip и создания каталога в его собственный метод.В дальнейшем это облегчит разгрузку в поток.Это также будет иметь дополнительное преимущество, заключающееся в том, что вызывающий абонент решит, хочет ли он запустить его в другом потоке или нет.

public void ExtractZip(ZipFile zip)
{
   var temp = FileUtils.GetTempDirectoryName();
   zip.ExtractToDirectory(temp);   // BLOCKING CALL

   if (Directory.Exists(folderPath))
   {
       Directory.Delete(folderPath, true);
   }

   var firstChild = Path.Combine(temp, folderName);
   Directory.Move(firstChild, folderPath);
   Directory.Delete(temp);
}

Затем метод верхнего уровня загрузит файл и распакует zip

// this method contains async IO code aswell as CPU bound code
// that has been offloaded to another thread
public async Task ProcessAsync()
{
   using (var zip = await downloader.DownloadAsZipArchive(downloadUrl))
   {
      // I would use Task.Run until it proves to be a performance bottleneck
      await Task.Run(() => ExtractZip(zip));
   }
}
0 голосов
/ 07 февраля 2019

Это немного неприятно делать это в Rx.Объединение Task<IDisposable> грубо.Вот что я получил:

Observable
    .FromAsync(() => downloader.DownloadAsZipArchive(downloadUrl))
    .SelectMany(z =>
        Observable
            .Using(() => z, zip => Observable.Start(() =>
            {
                var temp = FileUtils.GetTempDirectoryName();
                zip.ExtractToDirectory(temp);   // BLOCKING CALL

                if (Directory.Exists(folderPath))
                {
                    Directory.Delete(folderPath, true);
                }

                var firstChild = Path.Combine(temp, folderName);
                Directory.Move(firstChild, folderPath);
                Directory.Delete(temp);             
            })))
    .Subscribe();
0 голосов
/ 07 февраля 2019

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

1004 * Что вы можете сделать (спорно), это разгрузить егов пуле потоков вы будете подвергаться штрафу за поток потоков, что означает, что вы берете поток пула и блокируете его (используя ценные ресурсы).Однако, поскольку ожидается Task, он освободит контекст пользовательского интерфейса.
await Task.Run(() => zip.ExtractToDirectory(temp));

Примечание. Хотя это решит проблему, лучшим подходом здесь будет использование TaskCompletionSource.это события для задач (из-за отсутствия лучших слов), это избавит от необходимости связывать поток без необходимости

Обновление Отличный комментарий olitee

Чуть менее спорно ... вы могли бы расширить это использовать: * * * 1019 1020

await Task.Factory.StartNew(() => zip.ExtractToDirectory(temp), TaskCreationOptions.LongRunning); 
1022

, которая будет форсировать создание новой специальной нити для операции.Хотя за создание этого потока будет наложено дополнительное наказание, а не за повторное использование пула, но это не проблема для такой длительной операции, как эта.

...