2013-10-24 4 views
2

я получил следующий метод расширения:IEnumerable Метод с AsParallel

static class ExtensionMethods 
{ 
    public static IEnumerable<IEnumerable<T>> Subsequencise<T>(
     this IEnumerable<T> input, 
     int subsequenceLength) 
    { 
     var enumerator = input.GetEnumerator(); 
     SubsequenciseParameter parameter = new SubsequenciseParameter 
      { 
       Next = enumerator.MoveNext() 
      }; 

     while (parameter.Next) 
       yield return getSubSequence(
        enumerator, 
        subsequenceLength, 
        parameter);   
    } 

    private static IEnumerable<T> getSubSequence<T>(
     IEnumerator<T> enumerator, 
     int subsequenceLength, 
     SubsequenciseParameter parameter) 
    { 
     do 
     { 
      lock (enumerator) // this lock makes it "work" 
      {     // removing this causes exceptions. 
       if (parameter.Next) 
        yield return enumerator.Current; 
      } 

     } while ((parameter.Next = enumerator.MoveNext()) 
        && --subsequenceLength > 0); 
    } 

    // Needed since you cant use out or ref in yield-return methods... 
    class SubsequenciseParameter 
    { 
     public bool Next { get; set; } 
    } 
} 

Ее цель заключается в разделении последовательности на подпоследовательности заданного размера.

Назвать это так:

foreach (var sub in "abcdefghijklmnopqrstuvwxyz" 
        .Subsequencise(3) 
        .**AsParallel**() 
        .Select(sub =>new String(sub.ToArray())) 
{ 
    Console.WriteLine(sub); 
} 

Console.ReadKey(); 

работы, однако есть несколько пустых строк в-между, так как некоторые из нитей «слишком поздно» и ввести первое возвращение выход.

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

Очевидно, что этот пример не оправдывает использование как параллельного вообще. Это просто показать, как можно назвать метод.

ответ

3

Проблема заключается в том, что использование итераторов оценивается лениво, поэтому вы возвращаете лениво оцененный итератор, который используется из нескольких потоков.

Вы можете это исправить, переписав свой метод следующим образом:

public static IEnumerable<IEnumerable<T>> Subsequencise<T>(this IEnumerable<T> input, int subsequenceLength) 
{ 
    var syncObj = new object(); 
    var enumerator = input.GetEnumerator(); 
    if (!enumerator.MoveNext()) 
    { 
     yield break; 
    } 

    List<T> currentList = new List<T> { enumerator.Current }; 
    int length = 1; 
    while (enumerator.MoveNext()) 
    { 
     if (length == subsequenceLength) 
     { 
      length = 0; 
      yield return currentList; 
      currentList = new List<T>();     
     } 
     currentList.Add(enumerator.Current); 
     ++length; 
    } 
    yield return currentList; 
} 

Это выполняет ту же функцию, но не использует итератор для реализации «вложенных» IEnumerable<T>, избегая проблемы. Обратите внимание, что это также позволяет избежать блокировки, а также настраиваемого типа SubsequenciseParameter.

+0

Хороший ответ, однако стоит упомянуть, что он поставляется с ценой - накоплением. – BartoszKP

+0

@BartoszKP Накопление? Единственными накопленными значениями являются «вложенные» последовательности, которые будут довольно малы. Общий процесс по-прежнему передается ... Здесь должно быть очень мало «цены» (если, конечно, вы не имеете дело с * очень большими размерами последовательности) –

+0

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

Смежные вопросы