20 Haziran 2016 Pazartesi

Parallel Sınıfı

Giriş
Şu satırı dahil ederiz.
using System.Threading.Tasks;
Task Parallel Library (TPL) Task sınıfı ile artık thread'ler ile çalışmamıza gerek bırakmıyor.

Yine aynı şekilde TPL ile gelen Parallel sınıfı veriyapıları üzerinde maksimum çekirdek sayısını kullanmamızı sağlayan ve data parallelism için kullanılan Invoke(), For() ve ForEach() metodlarına sahip.  Task parallelism ve Data parallelism arasındaki fark şu:
  • Task parallelism is the simultaneous execution on multiple cores of many different functions across the same or different datasets.
  • Data parallelism (aka SIMD) is the simultaneous execution on multiple cores of the same function across the elements of a dataset.
Yani Data parallelism tek bir veri yapısı için işlemleri farklı çekirdeklere dağıtmak ve çalıştırrmakla ilgilenir.

Bu metodlar ile ilgili örnekler aşağıda. Çatıda kullanılan ParallelLoopState sınıfı örnekleri de mevcut.

1. For Metodu
For döngüsünün iki çeşidi var. İndex kullanmayan ve kullanan.

Index Kullanmadan Döngü
Örnek'te Method1 paralel olarak bir çok defa çağırılıyor.
Parallel.For(0, 1000000000, x => Method1());

Index Kullanarak Döngü
Örnek'te döngüde indekse erişim mümkün.
Not : Indeks kullanarak global bir dizinin elemanına erişmek bence doğru değil. Bunun yerine ForEach kullanılmalı!
Parallel.For(0, maxNumber, index => {
   // ....
});
For ve ParallelLoopState
ParallelLoopState nesnesi ile döngüyü durdurmak mümkün.
Örnek:
Parallel.For(0, maxNumber, (i, loopState) =>
{
    if (IsPrime((UInt32)i))
    {
        Interlocked.Increment(ref primesFound);
        if (primesFound > maxPrimes)
        {
            Console.WriteLine(“Last prime found: {0:N0}”,i);
            loopState.Stop();
        }
    }
});

2. ForEach Metodu
Parallel.ForEach metodu yazısına taşıdım.

3. Invoke Metodu
Parallel.Invoke metodu yazısına taşıdım.

ParallelOptions ile Kullanmak
Şu satırı dahil ederiz.
using System.Threading.Tasks;
Invoke, For, ForEach için kullanılabilir. En fazla kaç tane thread kullanılmasını istediğimiz belirtir. Bence bu sınıfı hiç kullanmamak gerekir. Örnek:
List<string> input = new List<string>();

static void exec(int threadcount)
{
  ParallelOptions options = new ParallelOptions();
  options.MaxDegreeOfParallelism = threadcount;
  Parallel.ForEach(input,options, item =>
  {..}
}
Bir başka örnek
Parallel.For(0, urlList.Count - 1, 
    new ParallelOptions { MaxDegreeOfParallelism = threads }, 
    item =>{...}
);

Timer ile Kullanmak
Herhangi bir paralel işin ne kadar kaldığını ölçmek her zaman karşımıza çıkabilecek bir istek. Örnek'te bir Timer başlatılarak her saniye ne kadar işin tamamlandığı gösteriliyor.
int count = 0;

TimeSpan reportPeriod = TimeSpan.FromMinutes(0.1);

using (new Timer(
    _ => Console.WriteLine("{0}: Extracted {1} urls", DateTime.Now, count),
    null, reportPeriod, reportPeriod))
{
    Parallel.ForEach(
        list, tuple =>
        {
            //download urls ...

            Interlocked.Increment(ref count);
        });
}

Console.WriteLine("{0}: Done", DateTime.Now);

Global State
İster For ister ForEach metodunu kullanalım eğer döngü içindeki sonuçları bir başka veriyapısına erişmek gerekirse, bu veri yapısının concurrent çalışıyor olmasına dikkat etmek gerekir. Bu amaç için ConcurrentBag, ConcurrentStack, ConcurrentQueue kullanılabilir. Örnek:
var pages = new ConcurrentBag<string>();
Parallel.ForEach(pageNodes, node =>
{
    try
    {
        string temp = DoSomeComplicatedModificationOnNode(node);
        pages.Add(temp);
    }
    catch (Exception)
    {
        throw;
    }
});

Hiç yorum yok:

Yorum Gönder