Я пытаюсь понять, почему иногда возникает взаимоблокировка при загрузке файла из-за разделения на несколько сегментов. Я использую async / await, но это работает не каждый раз. Есть идеи, как определить проблему?
IEnumerable<Task<bool>> downloadTasksQuery = from segment in job.Segments select RunSegment(segment);
Task<bool>[] downloadTasks = downloadTasksQuery.ToArray();
bool[] lengths = await Task.WhenAll(downloadTasks).ConfigureAwait(false);
после многократного ожидания не возвращается.
И метод RunSegment ()
Метод для скачать сегмент с помощью http клиента.
private async Task<bool> RunSegment(DownloadSegment segment)
{
try
{
int TotalBytesReadedInSession=0;
if (segment.LocalStream == null) return false;
segment.Status = DownloadStatus.Starting;
segment.IsBusy = true;
segment.Error = string.Empty;
segment.Speed = 0;
var request = new HttpRequestMessage(HttpMethod.Get, job.Url);
request.Headers.Range = new RangeHeaderValue(segment.Start + segment.BytesDownloaded, segment.End);
var response = await job.DownloadManager.httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token);
response.EnsureSuccessStatusCode();
using (var stream = await response.Content.ReadAsStreamAsync())
{
DateTime dtInitial = DateTime.Now;
var buffer = new byte[job.DownloadManager.bufferSize];
var isMoreDataToRead = true;
segment.Status = DownloadStatus.Downloading;
segment.IsBusy = true;
job.Status = DownloadStatus.Downloading;
do
{
try
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
var read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationTokenSource.Token);
TotalBytesReadedInSession += read;
if((DateTime.Now - dtInitial).TotalSeconds >= 1)
{
segment.Speed = TotalBytesReadedInSession / (DateTime.Now-dtInitial).TotalSeconds;
}
if (read == 0 || (segment.End - (segment.Start + segment.BytesDownloaded)) <= 0)
{
isMoreDataToRead = false;
if ((segment.End - (segment.Start + segment.BytesDownloaded)) <= 0)
{
segment.BytesDownloaded = (segment.End - segment.Start);
segment.Status = DownloadStatus.Completed;
segment.IsBusy = false;
}
return true;
}
else
{
// Write data on disk.
if (segment.End > 0 && (segment.Start + segment.BytesDownloaded + read) > segment.End)
{
// adjust the 'readSize' to write only necessary bytes
read = Convert.ToInt32((segment.End - segment.Start - segment.BytesDownloaded));
}
if (read > 0)
{
lock (segment.LocalStream)
{
segment.LocalStream.Seek(segment.Start + segment.BytesDownloaded, SeekOrigin.Begin);
}
await segment.LocalStream.WriteAsync(buffer, 0, read);
segment.BytesDownloaded += read;
segment.Progress = ((segment.BytesDownloaded) * 1d) / ((segment.End - segment.Start) * 1d) * 100;
}
else if ((segment.Start + segment.BytesDownloaded) >= segment.End) { segment.IsBusy = false; segment.Status = DownloadStatus.Completed; return true; }
}
}
catch (Exception ex)
{
segment.Speed = 0;
if (ex is OperationCanceledException)
{
segment.Error = string.Empty;
segment.Status = DownloadStatus.Stopped;
segment.IsBusy = false;
return false;
}
segment.CurrentTryError = +1;
segment.Status = DownloadStatus.Error;
segment.Error = ex.Message;
if (stream != null) { stream.Close(); stream.Dispose(); }
await Task.Delay(3000);
if ((segment.CurrentTryError - 1) > job.DownloadManager.MaximumTryErrorCounts) { segment.IsBusy = false; return false; }
return await RunSegment(segment).ConfigureAwait(false);
}
} while (isMoreDataToRead);
}
}
catch (Exception ex)
{
segment.Speed = 0;
segment.Status = DownloadStatus.Error;
segment.Error = ex.Message;
segment.IsBusy = false;
return false;
}
return true;
}