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 решил мою проблему.

Другие вопросы по тегам