Тупик возникает при загрузке файлов с сегментами в c# - PullRequest
1 голос
/ 18 июня 2020

Я пытаюсь понять, почему иногда возникает взаимоблокировка при загрузке файла из-за разделения на несколько сегментов. Я использую 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;
        }

Ответы [ 2 ]

0 голосов
/ 18 июня 2020

На самом деле я бы предположил, что весь этот раздел должен быть заблокирован на уровне файла (где несколько сегментов не записываются одновременно), и все будет в порядке.

     lock (job.FileLock)
     {
          segment.LocalStream.Seek(segment.Start + segment.BytesDownloaded, SeekOrigin.Begin);     
          await segment.LocalStream.WriteAsync(buffer, 0, read);
     }

0 голосов
/ 18 июня 2020

может это быть из-за файлового потока? Перед загрузкой создаю файловый поток. все сегменты записаны в один файл

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...