Условная блокировка IEnumerable‹t› дает возвращаемый результат

Я надеюсь, что кто-то может помочь мне найти более эффективное решение моей проблемы ниже. Я использую шаблон возврата IEnumerable yield в многопоточном конвейере. В большинстве случаев это работает отлично, однако у меня есть несколько сценариев, в которых мне нужно, чтобы операции в конвейере выполнялись синхронно, а не в параллельных потоках, чтобы получить правильный результат. (т. е. столкнулись с проблемами параллелизма).

Существующий код:

public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
{
    foreach (Row row in rows)
    {
        //do some work....
        yield return row;
    }
}

Я думаю о создании необязательного параметра, который контролирует блокировку и неблокировку вывода перечислителя; что-то вроде:

public override IEnumerable<Row> Execute(IEnumerable<Row> rows, bool BlockingExecution)
{
    if (BlockingExecution)
    {
        return BlockingExecute(rows);
    }
    else
    {
        return NonBlockingExecute(rows);
    }
 }

 private IEnumerable<Row> NonBlockingExecute(IEnumerable<Row> rows)
 {
    foreach (Row row in rows)
    {
        //do some work....
        yield return row;
    }
 }

 private IEnumerable<Row> BlockingExecute(IEnumerable<Row> rows)
 {
     List<Row> BlockingResult = new List<Row>();
     foreach(Row r in NonBlockingExecute(rows))
     {
         BlockingResult.Add(r);
     }
     return BlockingResult;
 }

В функции BlockingExecute кажется неэффективным создавать копию IEnumerable в списке, чтобы принудительно очистить весь конвейер. Есть лучший способ это сделать?


person Michael Meow    schedule 11.11.2014    source источник
comment
По сути, вы хотите либо перечислить заданное перечисляемое rows (предположительно возвращая результаты лениво - здесь это называется неблокирующим), либо вы хотите перечислить материализованное перечисляемое, например, rows.ToList(). Следовательно, вы должны иметь возможность использовать то, что уже доступно в среде .Net.   -  person Vikas Gupta    schedule 11.11.2014
comment
Примечание: перечислитель, созданный с помощью yield return, не является потокобезопасным, рассмотрите возможность правильной блокировки или что-то вроде разбиение с помощью Parallel.ForEach   -  person Alexei Levenkov    schedule 11.11.2014
comment
Я не понимаю вашего вопроса. Вы говорите, что есть проблемы с параллелизмом. Но в коде, который вы включили, нет ничего, что могло бы решить проблемы параллелизма. Какие проблемы параллелизма существуют? Как они связаны с перечислением коллекции? Каким образом материализация коллекции связана с решением проблем параллелизма?   -  person Peter Duniho    schedule 11.11.2014
comment
Привет, @peter-duniho, для получения полной информации об этом сценарии ознакомьтесь с проектом Rhino ETL с открытым исходным кодом: hibernatingrhinos.com/oss/rhino-etl Сценарий выглядит следующим образом. В конвейере ETL объединено несколько операций DBCommand. Первый выполняет операцию вставки, а последующий выполняет сопоставление между вставленными строками. Операция сопоставления требует, чтобы все строки были вставлены, чтобы получить правильный результат. При использовании ленивого выполнения некоторые совпадения пропускаются, но принудительная загрузка вставки DBCommand для обработки всех строк гарантирует правильность сопоставления.   -  person Michael Meow    schedule 11.11.2014
comment
@Andrew: никто не хочет видеть полную структуру. См. stackoverflow.com/help/mcve и stackoverflow.com/help/how-to-ask. Вы должны опубликовать вопрос, который будет коротким, но все же полностью заключает в себе суть любой проблемы, с которой вы столкнулись. Ничто в вашем последнем комментарии не объясняет, как материализация коллекции решает любую проблему параллелизма, хотя это предполагает, что возможно вы просто скорректировали профиль выполнения, чтобы сделать их менее вероятными. происходить.   -  person Peter Duniho    schedule 11.11.2014


Ответы (1)


В общем, «блокирующее выполнение» будет в некоторой степени неэффективным. Но я бы, вероятно, использовал ToArray для этой формы, а ваша неблокирующая версия немного сбивает с толку. Действительно ли он должен выполнять работу для каждого, чего не делает блокирующий?

Я бы, наверное, сделал это.

public override IEnumerable<Row> Execute(IEnumerable<Row> rows, bool BlockingExecution)
{
    if (BlockingExecution)
    {
        return rows.ToArray();
    }
    else
    {
        return NonBlockingExecute(rows); // or just "return rows". It seems like the best 
                                         // practice here, in most cases anyway
                                         // would be to move that work elsewhere
    }
 }

 private IEnumerable<Row> NonBlockingExecute(IEnumerable<Row> rows)
 {
    foreach (Row row in rows)
    {
        //do some work....
        yield return row;
    }
 }

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

person Matthew Haugen    schedule 11.11.2014
comment
это не делает ничего, кроме простого перебора перечислимого, как я предполагал, по крайней мере, так я интерпретировал комментарий //do some work..... - person svick; 11.11.2014
comment
@svick о боже, хорошая мысль. Я полностью пропустил это. Я исправил свой ответ. Спасибо что подметил это. - person Matthew Haugen; 11.11.2014