2010-07-23 10 views
5

kullanarak minimumlar arasında seçim yapma C#, Parallel.ForEach ve .NET genel olarak yeni. Binlerce yeri içeren bir aramayı paralel hale getirmek istiyorum. Her konum için büyük daire mesafesini hesaplıyorum. Bu, farklı çekirdeklere yayılmak istediğim bir hesap. Sorum şu, bu MSDN TPL example'da olduğu gibi yalnızca bir iş parçacığı değişkeni varsa bunu nasıl yapabilirim? sonuç için, ben Interlocked baktı ve onun seçenekleri Add, CompareExchange, Decrement, Exchange, Increment ve Read gördüm, ama sadece arttırarak, azaltarak veya eşitlik test ekleyerek değilim. Nesneyi, paralel olarak çalışan birkaç iş parçacığı üzerinde döndürmek istiyorum. Bu, en kısa toplam uzaklığa sahip. Benim bağırsak bu kolay olmalı, ben bir Location ve bir mesafe sarar bazı küçük bir nesne oluşturmak mümkün olmalıdır diyor, ama ben her iş parçacığı en iyi yanıtı ve sonra aralarında en kısa mesafeyi nasıl seçerim? İşte paralel olmayan versiyonu:Parallel.ForEach

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    double closest = double.MaxValue; 
    Location closestLoc = null; 
    foreach (Location aLoc in allLocations) 
    { 
    if (aLoc != myLocation) 
    { 
     double d = greatCircle(myLocation, aLoc); 
     if (d < closest) 
     { 
     closest = d; 
     closestLoc = aLoc; 
     } 
    } 
    } 
    return closestLoc; 
} 

İyi tavsiyelerde gibiydi bir DDJ Blog Post gördün, ama en iyi tavsiye olmadığını merak etti. Yazarın diziler üzerinde döngü yaptığını görüyorum ve bunu yapmanın daha işlevsel bir yolu olup olmadığını merak ediyorum. Fonksiyonel dünyada map, lambda ve min kullanırdım.

cevap

10

burada kolay seçenek PLINQ geçmek olacaktır:

söyleniyor
Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    return allLocations 
       .AsParallel() 
       .Min(location => greatCircle(myLocation, location)); 
} 

, bu temelde sadece aggregation with parallel constructs olduğunu. Parallel sınıfına bağlı kalmak istiyorsanız birkaç seçeneğiniz vardır. Kilitlemeyi kullanarak bunu kendiniz blokta senkronize etmek bir seçenek olacaktır. Genel performansınıza zarar vereceği için bunu tavsiye etmem.

Daha iyi seçenek, yerel durumu sağlayan Parallel.ForEach yöntemlerini kullanmaktır. Onlar bu yeniden olanak sağlayacak: Ben local state for aggregations in detail on my blog kullanarak kapak

Location findClosestLocation(Location myLocation, List<Location> allLocations) 
{ 
    double closest = double.MaxValue; 
    Location closestLoc = null; 
    object sync = new object(); 

    Parallel.ForEach<Location, Tuple<double,Location>(
     allLocations, 
    () => new Tuple(double.MaxValue, null), 
     (location, loopState, localState) => 
     { 
      double d = greatCircle(myLocation, aLoc); 
      if (d < localState.Item1) 
       return new Tuple(d, aLoc); 
      else 
       return localState; 
     }, 
     localState => 
     { 
      lock(sync) 
      { 
       if (localState.Item1 < closest) 
       { 
        closest = localState.Item1; 
        closestLoc = localState.Item2; 
       } 
      } 
     } 
); 
    return closestLoc; 
} 

. Bu işlem temel olarak, işlem başına bir kilit yerine iş parçacığı başına bir kilitleme işlemine değiştirir, böylece bir naif kilitleme çözümünden çok daha yüksek bir verim elde edersiniz.

+0

Vay, bu kadar kısa ve tatlı. Fonksiyonel kalbimi şarkı söylüyor. Teşekkürler! – gknauth

+0

@gknauth: Evet - (Doğru/performans) Parallel.ForEach seçeneğini de ekledim. Öğrenmeyi öneririm ** diğer seçenek nasıl çalışır ve onu anlamaya çalışmak. Yine de, bu operasyonların çoğu için, PLINQ'ı harika olduğu için kullanıyorum. –

+0

Beni blogunuza bağladığınız için teşekkürler, harika görünüyor. Bir kitap yaz, ben alırım! Ya da çok fazla çalışırsa, sana akşam yemeği alırım. – gknauth

İlgili konular