BlockingCollection не выполняется должным образом
У меня есть метод, который вызывает переданный Func
в фоновом потоке под названием TaskSpin
, Это делает один метод за один раз на основе сайта, на который нажали DataGridView
(DGV), и он ДОЛЖЕН делать ТОЛЬКО по одному за раз. Поэтому я нажимаю на сайт (ячейка кнопки в DGV), и он запускает метод на этом сайте. Сейчас у меня много сайтов, и я не хочу сотни раз кликать и ждать, вместо этого я хочу выполнить пакетную обработку всех сайтов, содержащихся в DGV. Я не могу использовать прямой цикл, чтобы запустить их, то есть
foreach (DataGridViewRow row in this.DGV)
TaskSpin(SomeMethod, SomeParameterArray);
так как это почти сразу же возвращается к вызывающей стороне и запускает следующий сайт, что не очень хорошо, так как они используют общие файлы. Поэтому мне нужен способ поставить сайты в очередь, чтобы они выполнялись один за другим. Я решил использовать ConncurrentCollection - BlockingCollection
Но я застрял. В каком-то событии нажатия кнопки у меня есть
BlockingCollection<Action<object[]>> taskQueue =
new BlockingCollection<Action<object[]>>();
List<object[]> paramQueue = new List<object[]>();
foreach (DataGridViewRow row in this.DataGridViewDrg.Rows)
{
paramQueue.Add(DrgDataRowInfo(row.Index));
Action<object[]> action = new Action<object[]>(AsyncMethod);
taskQueue.Add(action);
}
int i = 0;
foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
action(paramQueue[i]);
где AsyncMethod
запустил метод set в фоновом потоке, используя TPL.
private void AsyncMethod(object[] siteInfo)
{
TaskSpin(BuildDataForSite, siteInfo);
}
Я ожидаю, что это будет работать каждый Action<object[]>
один за другим. Но это просто замораживает мое приложение. Что я здесь не так делаю?
Спасибо за ваше время.
* Редактировать. TaskSpin *
public TaskSpin(Func asyncMethod, object[] methodParameters)
{
...
asyncTask = Task.Factory.StartNew<bool>(() =>
asyncMethod(uiScheduler, methodParameters));
asyncTask.ContinueWith(task =>
{
...
// Finish the processing update UI etc.
...
}
...
}
1 ответ
Код
foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
action(paramQueue[i]);
работает в вашем потоке пользовательского интерфейса, вызывая тем самым блокировку.
Похоже, ваше требование заключается в том, что вы хотите обрабатывать список вещей по одному в потоке, не являющемся пользовательским интерфейсом. Для этого с вашим текущим кодом и минимальной модификацией вы можете переместить вышеупомянутый foreach в отдельный поток, например, создав одну задачу, которая выполняет foreach.
Task.Factory.StartNew(() =>
{
foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
action(paramQueue[i]);
});
Хотя я опоздал
Мое решение - использовать Task.Run
вместо того Task.Factory.StartNew
Task.Run(() =>
{
foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
action(paramQueue[i]);
});
Я отправляю этот ответ на основе этой статьи
Я также использовал Task.Factory.StartNew
но затем он иногда запускался в моем потоке пользовательского интерфейса, вызывая зависание моего приложения. Task.Run
решил мою проблему.