@kosyak_47

C#. Как прописать ожидание выполнения другого потока?

Добрый день.

Есть метод (загрузка фотографий по URL-ссылкам), который я хочу выполнять в отдельном потоке, ибо у меня подвисает прога при каждом обращении по ссылке на сайт. Виснет на моменте обращения по первой ссылке (всего 300 ссылок) к изображению

//data[i].photo2 - прямая ссылка к .jpg
Stream stream = await httpClient.GetStreamAsync(data[i].photo2);
FileStream file = File.OpenWrite(PathForSavePhotos + "/" + data[i].codeProduct + "." + splitFileName[splitFileName.Length - 1]);
await stream.CopyToAsync(file);


После отвисания код почему то заходит в catch, где у меня прописан вывод окна с сообщением об ошибке,

try
{
...
}
catch
{
           MessageBox.Show("В программе произошла непредвиденная ошибка. Обратитесь к разработчику", "Непредвиденная ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}


но на этом программа не останавливается и продолжает выполнять загрузку фоток, но уже без захода в catch. Сама прога подвисает на секунду при каждом обращении на сайт. Поэтому решил добавить отдельный поток.

Может быть есть другие пути решения? Если нет, то как прописать в проге ожидание выполнения потока, в котором выполняется обращение к сайту для загрузки картинок?
  • Вопрос задан
  • 393 просмотра
Решения вопроса 1
gdt
@gdt
Программист
Мало контекста, у вас консольное приложение, WinForms или WPF? Если WinForms или WPF - зависание вызвано тем, что ваш код выполняется в UI потоке. Для начала можно попробовать добавить .ConfigureAwait(false) к вашим асинхронным вызовам, только потом нужно не забыть все операции с UI после асинхронных вызовов перенаправить в UI поток (Dispatcher.Invoke).

Далее, как уже верно подмечено в комментариях - блок try/catch должен быть внутри цикла (из вашего примера непонятно как он расположен).

Отвечая на ваш изначальный вопрос - скорее всего вам совершенно необязательно выделять отдельный поток, вы можете написать что-то вроде:
_downloadTask = Task.Run(MySuperDownloaderMethod);

Где MySuperDownloaderMethod - асинхронный метод, выполняющий загрузку файлов. Далее там, где вам нужно подождать - пишете
await _downloadTask;

Если не устраивает такой вариант или вам очень нужен выделенный отдельный поток, вы можете завести у себя в классе поле типа TaskCompletionSource<object> _downloadTcs = new();, и после скачивания всех изображений выполнить _downloadTcs.SetResult(), а там, где хотите подождать - вызвать await _downloadTcs.Task;

Если хочется чего-то взрослого, тогда используйте AutoResetEvent. Определяете в классе поле типа AutoResetEvent _downloadEvent = new(false);, после загрузки всех файлов вызываете _downloadEvent.Set(), там где нужно подождать - _downloadEvent.WaitOne();

С потоком что неудобно - вам придётся его явно ждать при помощи Thread.Join();, что не всегда удобно. К тому же, если поток не создан как Background и не завершён на момент завершения программы - программа будет ждать его завершения, что тоже не всегда удобно и очевидно. Поэтому, если нет жёсткой необходимости в потоке, лучше используйте Task.

Вообще рекомендую почитать www.albahari.com/threading - очень хорошее чтиво на тему многопоточности.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

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