2009-02-07 28 views
7

C# içinde paralel numaralar üzerinde foreach stil yineleme yapmanın bir yolu var mı? Abonelik listeleri için, indeks aralığı üzerinde bir int yineleyen düzenli bir for döngüsünü kullanabileceğini biliyorum, ancak bir takım nedenlerden dolayı foreach'u for'a tercih ediyorum.C# içinde paralel yineleme

Bonus puanlar hiçbir, C# 2.0

+0

wouldn' Bir döngü, aşağıdaki Kombine yanıtı yerine daha basit, daha kısa, okunabilir bir çözüm olabilir mi? Bu durumda foreach tercih nedenleriniz nelerdir – Gishu

+0

Ayrıca paralel iterasyon sadece Ruby 1.9'da ortaya çıktı, bu yüzden şu anda C# olmamaya bahse girerim. LISP bunu olsa da :) – Gishu

+0

Anladığımdan emin değilim doğru soru sor. Paralel olarak birden fazla numarada yineleme yapmaya mı çalışıyorsunuz yoksa paralel olarak farklı öğeleri işleyen bir numaralandırmaya mı çalışıyorsunuz? –

cevap

9

Kısa cevap çalışır eğer. foreach, aynı anda yalnızca bir numarada çalışır.

Ancak, paralel numaranızı tek bir tanede birleştirirseniz, birleştirilene göre foreach yapabilirsiniz. (Ben test etmedim rağmen) ben bunu yapmanın yöntemi inşa herhangi kolay farkında değilim, ama şu çalışmalıdır: Sonra

public IEnumerable<TSource[]> Combine<TSource>(params object[] sources) 
{ 
    foreach(var o in sources) 
    { 
     // Choose your own exception 
     if(!(o is IEnumerable<TSource>)) throw new Exception(); 
    } 

    var enums = 
     sources.Select(s => ((IEnumerable<TSource>)s).GetEnumerator()) 
     .ToArray(); 

    while(enums.All(e => e.MoveNext())) 
    { 
     yield return enums.Select(e => e.Current).ToArray(); 
    } 
} 

yapabilirsiniz döndürülen numaralandırılabilir üzerinde foreach:

foreach(var v in Combine(en1, en2, en3)) 
{ 
    // Remembering that v is an array of the type contained in en1, 
    // en2 and en3. 
} 
+0

Neden IEnumerable yerine parametre türü olarak [] nesnesini seçtiniz? Bu istisnayı ortadan kaldıracak, değil mi? – mafu

+0

C# dilinin en az bir sürümü, paramları nesne [] dışında bir şeyle derlemeyecek. Bu yanıtı dikkate alındığında beş yaşındadır, ben bu noktada kullanılan ediyorum sadece versiyonunu tahmin ediyorum IEnumerable 'parametreleri içeren işe yaramaz biri [] oldu sources'. Bu günlerde elbette daha açık yazmayı kullanırdım. Muhtemelen de iki enumerable için 'IEnumerable .Zip' uzatma yöntemi kullanmak istiyorum - bu da beş yıl önce yoktu oldukça emin. – Zooba

0

Bu sizin için çalışır mı?

public static class Parallel 
{ 
    public static void ForEach<T>(IEnumerable<T>[] sources, 
            Action<T> action) 
    { 
     foreach (var enumerable in sources) 
     { 
      ThreadPool.QueueUserWorkItem(source => { 
       foreach (var item in (IEnumerable<T>)source) 
        action(item); 
      }, enumerable); 
     } 
    } 
} 

// sample usage: 
static void Main() 
{ 
    string[] s1 = { "1", "2", "3" }; 
    string[] s2 = { "4", "5", "6" }; 
    IEnumerable<string>[] sources = { s1, s2 }; 
    Parallel.ForEach(sources, s => Console.WriteLine(s)); 
    Thread.Sleep(0); // allow background threads to work 
} 

C# 2.0 için, yukarıdaki lambda ifadelerini delegelere dönüştürmeniz gerekir.

Not: Bu yardımcı program, arka plan iş parçacığı kullanır. Ön plan iş parçacığı kullanmak için değiştirmek isteyebilirsiniz ve muhtemelen tüm iş parçacıkları bitirene kadar beklemek isteyeceksiniz. Bunu yaparsanız, sources.Length - 1 iş parçacığı oluşturmanızı ve son (veya ilk) kaynak için geçerli yürütme iş parçacığını kullanmanızı öneririm.

(Ben ipler benim kodunda bitmesini bekleyen dahil isterdim, ama ben henüz. Sana bir WaitHandleThread.Join(). Kullanmalıdır tahmin Bunun nasıl yapılacağını bilmiyorum üzgünüm)

11

.NET 4'ün BlockingCollection bunu oldukça kolaylaştırır. Bir BlockingCollection oluşturun, .GetConsumingEnumerable() değerini sayısız yönteme döndürün. Daha sonra foreach sadece engelleme koleksiyonuna ekler.

E.g. Bu yardımcı olur

private BlockingCollection<T> m_data = new BlockingCollection<T>(); 

public IEnumerable<T> GetData(IEnumerable<IEnumerable<T>> sources) 
{ 
    Task.Factory.StartNew(() => ParallelGetData(sources)); 
    return m_data.GetConsumingEnumerable(); 
} 

private void ParallelGetData(IEnumerable<IEnumerable<T>> sources) 
{ 
    foreach(var source in sources) 
    { 
     foreach(var item in source) 
     { 
      m_data.Add(item); 
     }; 
    } 

    //Adding complete, the enumeration can stop now 
    m_data.CompleteAdding(); 
} 

Umut. BTW ben posted a blog about this dün gece

Andre

+1

Bu, şu anda doğru olarak işaretlenmelidir. Net 4.0 çıktı –

+0

Bu, birden çok numaralandırmayı bir araya getirmekle ilgili soruyu yanıtlamıyor. –

+0

Kesinlikle haklısınız Arthur, düzelteceğim – Andre

3

Ben .NET4 Paralel kitaplığından) EachParallel (bir uygulamasını yazdı. .NET 3 ile uyumludur.5: Parallel ForEach Loop in C# 3.5 Kullanımı:

string[] names = { "cartman", "stan", "kenny", "kyle" }; 
names.EachParallel(name => 
{ 
    try 
    { 
     Console.WriteLine(name); 
    } 
    catch { /* handle exception */ } 
}); 

Uygulama:

/// <summary> 
/// Enumerates through each item in a list in parallel 
/// </summary> 
public static void EachParallel<T>(this IEnumerable<T> list, Action<T> action) 
{ 
    // enumerate the list so it can't change during execution 
    list = list.ToArray(); 
    var count = list.Count(); 

    if (count == 0) 
    { 
     return; 
    } 
    else if (count == 1) 
    { 
     // if there's only one element, just execute it 
     action(list.First()); 
    } 
    else 
    { 
     // Launch each method in it's own thread 
     const int MaxHandles = 64; 
     for (var offset = 0; offset < list.Count()/MaxHandles; offset++) 
     { 
      // break up the list into 64-item chunks because of a limitiation    // in WaitHandle 
      var chunk = list.Skip(offset * MaxHandles).Take(MaxHandles); 

      // Initialize the reset events to keep track of completed threads 
      var resetEvents = new ManualResetEvent[chunk.Count()]; 

      // spawn a thread for each item in the chunk 
      int i = 0; 
      foreach (var item in chunk) 
      { 
       resetEvents[i] = new ManualResetEvent(false); 
       ThreadPool.QueueUserWorkItem(new WaitCallback((object data) => 
       { 
        int methodIndex = (int)((object[])data)[0]; 

        // Execute the method and pass in the enumerated item 
        action((T)((object[])data)[1]); 

        // Tell the calling thread that we're done 
        resetEvents[methodIndex].Set(); 
       }), new object[] { i, item }); 
       i++; 
      } 

      // Wait for all threads to execute 
      WaitHandle.WaitAll(resetEvents); 
     } 
    } 
} 
1

sopa temelleri isterseniz - Ben daha basit bir şekilde şu anda kabul cevabı yazdım:

public static IEnumerable<TSource[]> Combine<TSource> (this IEnumerable<IEnumerable<TSource>> sources) 
    { 
     var enums = sources 
      .Select (s => s.GetEnumerator()) 
      .ToArray(); 

     while (enums.All (e => e.MoveNext())) { 
      yield return enums.Select (e => e.Current).ToArray(); 
     } 
    } 

    public static IEnumerable<TSource[]> Combine<TSource> (params IEnumerable<TSource>[] sources) 
    { 
     return sources.Combine(); 
    }