Почему комбинация 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);