@monastrel

Как перенести выполнение кода с Queue и yield в поток (Thread/Task/etc), чтобы не морозить форму?

Добрый день.

Есть C# WinForms .NET 4 - 4.5 приложение. Есть следующий код:
public class SuperClass: ClassName
{}

public abstract class ClassName
{
      public static IEnumerable<ClassName> GetAll(string param)
      {
           var queue = new Queue<string>();
           var cacheableData = new HashSet<string>();
           queue.Enqueue(param);

           while (queue.Count > 0)
           {
               HttpWebRequest request = new HttpWebRequest.Create(param);
               // ... логика обработки http запроса
               yield return new SubClass {}

               // ... еще логика разная
               // paramNext = ...
               cacheableData.Add(paramNext);
               queue.Enqueue(paramNext);
           }
      }

      public class SubClass : ClassName
      { }
}

// .. выполнение кода на форме

var classResult = ClassName.GetAll("xxx");

foreach (ClassName result in classResult )
{
   /// что-то делаю с result  , заполняю инфу в ListBox
}


Его суть - получить некое значение, обработать его, добавить новые параметры в очередь, и так пока не получу все данные (пусть это будет какой-нить web spider, который парсит линки на страничке и добавляет их в очередь, или какие-нить email-ы).

При выполнении такого кода приложение, естественно, зависает (замораживается). Не спасает даже:

Application.DoEvents();

Думал о том, чтобы вынести выполнение в кода в поток (Thread, Task, может быть, еще что-то, я еще в этом теме не очень). Но не знаю, как это сделать (и можно ли с yield, или надо будет писать какие-то события, делегаты, может быть что-то еще). Помогите разобраться с такой задачей, пож-ста.

Спасибо!
  • Вопрос задан
  • 2644 просмотра
Пригласить эксперта
Ответы на вопрос 3
Kerman
@Kerman
Лучше воспользоваться такой штукой, как BackgroundWorker. У него есть событие ProgressChanged, через которое можно возвращать ClassName в основной поток.
Ответ написан
Комментировать
@monastrel Автор вопроса
2 Kerman

Я попробовал сделать так:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            foreach (ClassName result in SuperClass.GetAll("xxx"))
            {
                worker.ReportProgress(0, result );
            }
        }

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            ClassName result = (ClassName )e.UserState;

            listBox1.Items.Add(result.Param);
        }


Но код перестал работать (код класса не менялся вообще). Точнее начала появляться информация, которой не было без BackgroundWorker O_o Может быть асинхронность виновата? Или не знаю что
Ответ написан
@gleb_kudr
Два варианта.

1. BackgroundWorker. Это отдельный тред, делает работу (метод work) в фоновом потоке, управление возвращается в материнский поток либо по изменении прогресса, либо в конце работы. Примеры есть в msdn. Только помните о потокобезопасности - не меняйте коллекцию из другого треда в процессе работы.

2. Async/await паттерн из .net 4.5 Эта конструкция не создает отдельных тредов и позволяет все делать в основном потоке, поэтому потокобезопасна по сути. В msdn так же есть куча примеров.

Лично я рекомендую просто скопипастить примеры из msdn и потом постепенно менять на свой код, чтобы понять, в чем суть методов и в чем могут быть проблемы. BackgroundWorker довольно сложный паттерн, с ним лучше разобраться сначала на элементарном примере.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы