2012-03-19

SequentialPartitioner

Poniższy Partitioner zwraca elementy w kolejności w jakiej występują one w źródle enumeracji, niezależnie na ile wątków dane będą dzielone. Inaczej mówiąc enumeratory sub-podziałów zwracają elementy w kolejności jak w źródłowej kolekcji niezależnie od tego jak te elementy z sub-podziałów wyciągamy.

Narzut synchronizacji dla takiej metody jest dosyć duży. Nie jest ona także przyjazna dla pamięci cache. Zastosowanie takiej metody podziału to np. pobieranie plików z sieci:

  • przetwarzanie pojedynczego elementu jest długotrwałe
  • wątek przez większość czasu jest zablokowany
  • chcemy by elementy były pobierane w porządku występowania w kolekcji

GetDynamicPartitions() może być wywołany więcej niż jeden raz w trakcie życia SequentialPartitioner. Stąd w tej metodzie za każdym razem musimy tworzyć nasz nowy współdzielony enumerator. Umieszczenie go w obiekcie było by błędem.

yield return nie umieszczamy w bloku lock gdyż wynik zostanie zwrócony podczas założonej blokady co w praktyce oznacza sekwencyjne przetwarzanie elementów kolekcji.

Wywołanie return GetDynamicPartitions(m_source.GetEnumerator()); jest konieczne. Jeśli enumerator będziemy tworzyć w głównej metodzie to za każdym wywołaniem metody (pobraniem enumeratora dla osobnego wątku) zostanie on utworzony na nowo.

public class SequentialPartitioner<T> : 
    Partitioner<T>
{
    private readonly IList<T> m_source;

    public SequentialPartitioner(IList<T> a_source)
    {
        m_source = a_source;
    }

    public override bool SupportsDynamicPartitions
    {
        get
        {
            return true;
        }
    }

    public override IList<IEnumerator<T>> 
        GetPartitions(int a_partition_count)
    {
        var dp = GetDynamicPartitions();
        return (from i in Enumerable.Range(0, a_partition_count)
                select dp.GetEnumerator()).ToList();
    }

    public override IEnumerable<T> GetDynamicPartitions()
    {
        return GetDynamicPartitions(m_source.GetEnumerator());
    }

    private static IEnumerable<T> 
        GetDynamicPartitions(IEnumerator<T> a_enumerator)
    {
        while (true)
        {
            T el;
            lock (a_enumerator)
            {
                if (a_enumerator.MoveNext())
                    el = a_enumerator.Current;
                else
                    yield break;
            }

            yield return el;
        }
    }
}

Brak komentarzy:

Prześlij komentarz