У меня очень странная проблема. Мой WebClient.DownloadDataCompleted
большую часть времени не срабатывает.
Я использую этот класс:
public class ParallelFilesDownloader
{
public Task DownloadFilesAsync(IEnumerable<Tuple<Uri, Stream>> files, CancellationToken cancellationToken)
{
var localFiles = files.ToArray();
var tcs = new TaskCompletionSource<object>();
var clients = new List<WebClient>();
cancellationToken.Register(
() =>
{
// Break point here
foreach (var wc in clients.Where(x => x != null))
wc.CancelAsync();
});
var syncRoot = new object();
var count = 0;
foreach (var file in localFiles)
{
var client = new WebClient();
client.DownloadDataCompleted += (s, args) =>
{
// Break point here
if (args.Cancelled)
tcs.TrySetCanceled();
else if (args.Error != null)
tcs.TrySetException(args.Error);
else
{
var stream = (Stream)args.UserState;
stream.Write(args.Result, 0, args.Result.Length);
lock (syncRoot)
{
count++;
if (count == localFiles.Length)
tcs.TrySetResult(null);
}
}
};
clients.Add(client);
client.DownloadDataAsync(file.Item1, file.Item2);
}
return tcs.Task;
}
}
И когда я вызываю DownloadFilesAsync
в LINQPad изолированно, DownloadDataCompleted
вызывается примерно через полсекунды, как и ожидалось.
Однако в моем реальном приложении он просто не срабатывает, а код, ожидающий его завершения, просто зависает. У меня есть две точки останова, как указано в комментариях. Ни один из них не поражен.
Ах, но иногда он стреляет. Тот же URL, тот же код, просто новый сеанс отладки. Нет шаблона вообще.
Я проверил доступные потоки из пула потоков: workerThreads > 30k, completePortThreads = 999.
Я добавил сон на 10 секунд перед возвратом и проверил после сна, что мои веб-клиенты не были собраны мусором и что мой обработчик событий все еще подключен.
Теперь у меня закончились идеи, как решить эту проблему.
Что еще может быть причиной такого странного поведения?
clients
выйдет за рамки и, вероятно, будет поражен сборщиком мусора. Однако вы указали, что когда вы спите в методе,clients
, очевидно, все еще рядом. Если вы добавите сон, загрузка будет работать? Или вы все еще получаете такое же поведение? Кроме того, как долго вы ожидаете, что все загрузки займут? - person steve cook   schedule 01.09.2014clients
не выходит из области действия из-за действия, переданногоcancellationToken.Register
. - person Daniel Hilgarth   schedule 01.09.2014DownloadFilesAsync(......).Wait()
- person L.B   schedule 01.09.2014Task.WaitAll
, который ждет этой и других задач. Однако (1) я не понимаю, почему это повлияет на асинхронную загрузку - пожалуйста, уточните - и (2) проблема не исчезнет, когда я добавлю сон, и поэтомуTask.WaitAll
не будет вызываться. - person Daniel Hilgarth   schedule 01.09.2014await Task.WhenAll
), не блокируя вызывающий поток (я подозреваю, что из-за тупика). - person L.B   schedule 01.09.2014var html = new WebClient().DownloadStringTaskAsync("http://stackoverflow.com").Result;
(приложение Winforms) - person L.B   schedule 01.09.2014DownloadStringTaskAsync
илиDownloadDataTaskAsync
. Я использую вариант, который не возвращает задачу. Задача, которую я возвращаю из своего метода, — это задача TaskCompletionSource, не связанная с WebClient. - person Daniel Hilgarth   schedule 01.09.2014DownloadDataCompleted
запускается в контексте синхронизации вызывающего потока, то у вас та же проблема. - person L.B   schedule 01.09.2014Task.WhenAll
. Должен ли я эмулировать Task.WhenAll с потоком, который я запускаю сам? Я пытался использовать Task.Factory.ContinueWhenAll, но это не решило проблему. Я убедился, что в моем коде не осталось Task.WaitAll. - person Daniel Hilgarth   schedule 01.09.2014DownloadDataCompleted
не вызывается.ContinueWith
на что? Обратите внимание, я не используюDownloadDataTaskAsync
. Я используюDownloadDataAsync
. Он возвращаетvoid
, а неTask
. - person Daniel Hilgarth   schedule 01.09.2014