Асинхронная задача внутри цикла parallel.for

У меня есть асинхронный метод, вызываемый с кодом Parallel.For ниже. Теперь, глядя на код, он довольно прост, за исключением того, что класс JsonParse имеет статический метод, все, что он делает, это вызывает веб-службу для загрузки строки json, преобразует ее в объект PairResults и возвращает.

Проблема, с которой я сталкиваюсь, заключается в том, что цикл Parallel.For никогда не выходит, я ясно вижу, что данные поступают из веб-вызова «item.part1 = data.value», все работает нормально, но updateAllResults никогда не завершается. Что я делаю не так?

public void updateAllResults()
{ 
    Parallel.For(0, PairList.Count(), (i) =>
    {            
         var item = PairList[i];
         var data = (Parse.JsonParse<PairResults>
                             .getJsonString("http://localhost:22354/" 
                                                        + item.Original)).Result;
         item.part1 = data.value;
    }); 
}

person Sanj    schedule 14.03.2014    source источник
comment
Это должно завершиться, но это может занять много времени в зависимости от количества элементов в PairList. При этом рассмотрите возможность сделать это действительно асинхронным, используя async (i) => и await без вызова .Result.   -  person Kris Vandermotten    schedule 14.03.2014
comment
Ах, это сработало как по волшебству. изменение метода на async (i) =› было ответом.   -  person Sanj    schedule 14.03.2014


Ответы (2)


Это антипатерн для Parallel.For, потому что вы используете его только для запуска асинхронных операций и их блокировки. Таким образом, он заблокирует ограниченное количество потоков пула, и фактический уровень параллелизма может быть намного ниже, чем если бы вместо этого вы использовали задачи:

public void updateAllResults()
{
    var tasks = PairList.Select(async (item) => 
    {
        var data = await Parse.JsonParse<PairResults>
            .getJsonString("http://localhost:22354/" + item.Original).
            .ConfigureAwait(false);

        item.part1 = data.value;
    });

    Task.WaitAll(tasks.ToArray());
}

Кроме того, ваш вопрос помечен тегом winforms. И Parallel.For, и приведенный выше код заблокируют ваш поток пользовательского интерфейса, если вы используете его в приложении WinForms. Правильным решением было бы использовать Task.WhenAll:

public async Task updateAllResults()
{
    var tasks = PairList.Select(async (item) => 
    {
        var data = await Parse.JsonParse<PairResults>
            .getJsonString("http://localhost:22354/" + item.Original)
            .ConfigureAwait(false);

        item.part1 = data.value;
    });

    await Task.WhenAll(tasks.ToArray());
}

// button click handler
async void button_Click(object s, EventArgs e)
{
    this.button.Enabled = false;
    try
    {
        await updateAllResults()
    }
    finally
    {
        this.button.Enabled = true;
    }
}
person noseratio    schedule 14.03.2014

Parallel.For блокируется до тех пор, пока не будут выполнены все итерации или пока цикл не прервется, как обычный оператор for. Убедитесь, что все данные загружены. После этого Parallel.For должен завершиться и, следовательно, метод тоже.

person Emond Erno    schedule 14.03.2014