Почему комбинация Backgroundeworker + BlockingCollection медленнее?

У меня есть программа для доступа к базе данных и загрузки изображений. Я использовал BlockingCollection для этой цели. Однако для доступа к некоторым элементам пользовательского интерфейса я решил использовать комбинацию Backgroundworker а также BlockingCollection, Это значительно уменьшило скорость обработки по сравнению со скоростью, когда только Blockingcollection использовался. В чем может быть причина? Или, поскольку я сейчас получаю доступ к элементам пользовательского интерфейса, скорость снижается?

Вот код, над которым я работаю:

 private void button_Start_Click(object sender, System.EventArgs e)
    {
        BackgroundWorker bgWorker = new BackgroundWorker();
        bgWorker.DoWork += bw_DoWork;
        bgWorker.RunWorkerCompleted += bw_RunWorkerCompleted;
        bgWorker.ProgressChanged += bw_ProgressChanged;

        bgWorker.WorkerSupportsCancellation = true;
        bgWorker.WorkerReportsProgress = true;

        Button btnSender = (Button)sender;
        btnSender.Enabled = false;

        bgWorker.RunWorkerAsync();
    }

а также Do_Work() как следует:

{
        HttpWebRequest request = null;
        using (BlockingCollection<ImageFileName> bc = new BlockingCollection<ImageFileName>(30))
        {
            using (Task task1 = Task.Factory.StartNew(() =>
            {

                foreach (var fileName in fileNames)
                {

                        string baseUrl = "http://some url";
                        string url = string.Format(baseUrl, fileName);
                        request = (HttpWebRequest)WebRequest.Create(url);
                        request.Method = "GET";
                        request.ContentType = "application/x-www-form-urlencoded";
                        var response = (HttpWebResponse)request.GetResponse();
                        Stream stream = response.GetResponseStream();
                        img = Image.FromStream(stream);
                        FileNameImage = new ImageFileName(fileName.ToString(), img);
                        bc.Add(FileNameImage);
                        Thread.Sleep(100);
                        Console.WriteLine("Size of BlockingCollection: {0}", bc.Count);
                    }



            }))
            {
                using (Task task2 = Task.Factory.StartNew(() =>
                {


                    foreach (ImageFileName imgfilename2 in bc.GetConsumingEnumerable())
                    {
                        if (bw.CancellationPending == true)
                        {
                            e.Cancel = true;
                            break;
                        }
                        else
                        {
                            int numIterations = 4;
                            Image img2 = imgfilename2.Image;
                            for (int i = 0; i < numIterations; i++)
                            {
                                img2.Save("C:\\path" + imgfilename2.ImageName);
                                ZoomThumbnail = img2;
                                ZoomSmall = img2;
                                ZoomLarge = img2;
                                ZoomThumbnail = GenerateThumbnail(ZoomThumbnail, 86, false);
                                ZoomThumbnail.Save("C:\\path" + imgfilename2.ImageName + "_Thumb.jpg");
                                ZoomThumbnail.Dispose();
                                ZoomSmall = GenerateThumbnail(ZoomSmall, 400, false);
                                ZoomSmall.Save("C:\\path" + imgfilename2.ImageName + "_Small.jpg");
                                ZoomSmall.Dispose();
                                ZoomLarge = GenerateThumbnail(ZoomLarge, 1200, false);
                                ZoomLarge.Save("C:\\path" + imgfilename2.ImageName + "_Large.jpg");
                                ZoomLarge.Dispose();

                                //  progressBar1.BeginInvoke(ProgressBarChange);
                                int percentComplete = (int)(((i + 1.0) / (double)numIterations) * 100.0);
                                //if (progressBar1.InvokeRequired)
                                //{
                                //    BeginInvoke(new MethodInvoker(delegate{bw.ReportProgress(percentComplete)};))
                                //}
                            }
                            Console.WriteLine("This is Take part and size is: {0}", bc.Count);
                        }
                    }


                }))
                    Task.WaitAll(task1, task2);


            }

        }

    }

1 ответ

Решение

Лучшим вариантом может быть получение данных и запись их на диск синхронно, а вместо этого использовать Parallel.ForEach() разрешить отправку нескольких запросов одновременно. Это должно уменьшить количество ожидания в паре мест:

  • Нет необходимости ждать завершения одного HTTP-запроса перед отправкой последующих запросов.
  • Нет необходимости блокировать эту коллекцию BlockingCollection
  • Не нужно ждать завершения одной записи на диск, прежде чем запустить следующую.

Так что, возможно, что-то вроде этого:

Parallel.ForEach(fileNames, 
    (name) => 
    {
        string baseUrl = "http://some url";
        string url = string.Format(baseUrl, fileName);
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "GET";
        request.ContentType = "application/x-www-form-urlencoded";
        var response = (HttpWebResponse)request.GetResponse();
        Stream stream = response.GetResponseStream();
        var img = Image.FromStream(stream);

        // Cutting out a lot of steps from the 2nd Task to simplify the example
        img.Save(Path.Combine("C:\\path", fileName.ToString()));  
    });

Одна из возможных проблем, с которой вы можете столкнуться при таком подходе, состоит в том, что он начнет генерировать слишком много запросов одновременно. Это может привести к конфликту ресурсов или, возможно, веб-сервер будет интерпретировать его как вредоносное поведение и перестанет отвечать на ваши запросы. Вы можете ограничить количество запросов, которые происходят одновременно, установив MaxDegreeOfParallelism, В следующем примере показано, как можно ограничить операцию одновременной обработкой не более 4 файлов.

var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.ForEach(fileNames, (name) => { /* do stuff */ }, options); 
Другие вопросы по тегам