İlkay İlknur

just a developer...

.NET Framework 4.0 - Parallel.For ve Parallel.Foreach ile Döngülerin Paralel Olarak İşletilmesi

Merhaba Arkadaşlar,

.NET Framework 4.0 ile beraber gelecek olan paralel programlama altyapısına göz atacağımız ilk yazımızda basit olarak for ve foreach döngülerini nasıl paralel bir şekilde çalıştırabiliriz bunu inceliyor olacağız. Paralel olarak for ve foreach döngülerini çalıştırmak için TPL(Task Parallel Library) içerisinde bulunan metotları kullanıyor olacağız. TPL içerisinde bulunan For ve Foreach metotları sayesinde düngülerin paralel bir şekilde işletilmesi için gereken tüm işlemler TPL tarafından otomatik olarak ele alınmaktadır. Bizim yapmamız gereken ise sadece ilgili değerleri vermek. Öncelikle for ve foreach döngülerini nasıl paralel bir şekilde çalıştırabiliriz bunu incelemeye çalışalım. Paralel döngüler için Parallel tipi içerisinde bulunan For ve Foreach metotlarını kullanıyoruz. For metodunun parametrelerine bakarsak.

Parallel.For(int fromInclusive, int toExclusive, Action<int> body)

  • fromInclusive : For döngüsünün başlayacağı değer.
  • toExclusive : For döngüsünün döneceği son değer.
  • body : For döngüsünün içerisinde yapılacak işlemler.
Body parametresinin tipi Action<int> olarak görülmektedir. Yani biz bu parametre için C# 3.0 ile beraber gelen lambda ifadelerini kullanabiliriz.Wink Temel kullanım ise şu şekilde:
 //0'dan 9'a kadar dönen for döngüsü
Parallel.For(0,10,p=>
{
   //İşlemler
});
Foreach metoduna baktığımızda ise yine for döngüsüne benzer bir imza ile karşılaşıyoruz. Parallel.Foreach(IEnumerable<TSource> source, Action<TSource> body)
  • source : Foreach işlemi boyunca içerisinde dolaşacağımız IEnumerable arayüzünü implemente etmiş olan koleksiyon
  • body : Foreach döngüsü içerisinde ele alacağımız işlemler.
Foreach döngüsünün de temel kullanımı şu şekilde:
IEnumerable<int> numbers = Enumerable.Range(1,1000);
Parallel.ForEach(numbers, p =>
    {
        //numbers hangi tipi taşıyan koleksiyon ise 
       //p parametresi de o tipte
      //işlemler
    });
Basit olarak for ve foreach döngülerinin nasıl parallelize edildiğini görmüş olduk. Şimdi ise bir örnek üzerinde for ve foreach döngülerini kullanalım ve biraz daha derine inerek ne gibi işlemler gerçekleşiyor ve döngüler nasıl işletiliyor incelemeye çalışalım. Aşağıdaki gibi bir Çalışan sınıfımız olduğunu düşünelim ve çalışanların toplam alacağı ücretleri paralel bir şekilde hesaplayalım ve daha sonra ekrana yazdıralım.
public class Calisan
{
    public int ID { get; set; }
    public double SaatlikUcret { get; set; }
    public double ToplamCalisma { get; set; }
}
Elimizde çeşitli değerler ile yaratılmış olan bir Calisan listesi olduğunu düşünelim ve paralel foreach döngüsü ile gerekli hesaplamayı yaparak Console'a sonuçları yazdıralım.
static void Hesapla(List<Calisan> calisanlar)
{
    Parallel.ForEach(calisanlar, calisan =>
            {
               double total = calisan.SaatlikUcret * calisan.ToplamCalisma;
               Console.WriteLine("{0} numaralı çalışanın alacağı toplam ücret:{1}\n",calisan.ID,total);
            });
}
Son olarak listeyi yaratacağımız ve çeşitli çalışan nesneleri ile doldurup Hesapla metodunu çağıracağımız Main metot ise şu şekilde.
static void Main(string[] args)
{
    List<Calisan> calisanlar = new List<Calisan>()
    {
        new Calisan(){ID=1,SaatlikUcret=10,ToplamCalisma=30},
        new Calisan(){ID=2,SaatlikUcret=12,ToplamCalisma=34},
        new Calisan(){ID=3,SaatlikUcret=8,ToplamCalisma=38},
        new Calisan(){ID=4,SaatlikUcret=14,ToplamCalisma=39},
        new Calisan(){ID=5,SaatlikUcret=18,ToplamCalisma=37},
    };
    Hesapla(calisanlar);
}
Uygulamayı çalıştırıp sonuçlara bakarsak. Gördüğümüz gibi listeye çalışan nesnelerini ID'leri sıralı olacak şekilde eklememize rağmen foreach döngüsü içerisinde farklı sıralarda işletildiğini görmekteyiz. Uygulamayı her çalıştırdığımızda ise sürekli olarak farklı sonuçlarla karşılaşmamız oldukça normaldir. Bunun nedeni ise foreach döngüsü içerisinde paralel işlemek için yaratılan threadlerin işlemci tarafından farklı sıralarda işleme alınmasıdır. Bunu görmek için Hesapla metodunu aşağıdaki gibi değiştirip uygulamayı yeniden çalıştırırsak daha kolay bir şekilde threadleri takip edebiliriz.
static void Hesapla(List<Calisan> calisanlar)
{
    Parallel.ForEach(calisanlar, calisan =>
            {
                 int threadid = Thread.CurrentThread.ManagedThreadId;
                 double total = calisan.SaatlikUcret * calisan.ToplamCalisma;
                 Console.WriteLine("İşlem {0} nolu thread tarafından gerçekleştirildi.\n
                 {1} numaralı çalışanın alacağı toplam ücret:{2}\n",threadid,calisan.ID,total);
            });
}
Çıkan sonuçlara baktığımızda hemen hemen herbir çalışanın toplam alacağı ücret farklı bir thread üzerinden hesaplandığını görmekteyiz. Tabi her çalıştırdığımızda farklı bir sonuç görmemiz normal bir durumdur. Bazen tek bir thread id'ye sahip olan bir thread üzerinden de işlemler yapılabilir. Yazımızın sonuna gelirken kısaca bir özetlersek basit olarak for ve foreach döngülerini Task Parallel Library kullanarak nasıl parallelleştirebiliriz bu konu üzerinde durduk ve birkaç basit örnek yaptık. İlerleyen yazılarda da daha kompleks senaryolar üzerinde duracağız ve döngülerin paralelleştirilmesini daha da detaylı bir şekilde inceleyeceğiz. Şimdilik bu kadar, Görüşmek Üzere,


.NET Framework 4.0 - Paralel İterasyonların Break veya Stop Metotları ile Sonlandırılması

Merhaba Arkadaşlar,

Yaklaşık 1-1,5 haftadır bloguma herhangi birşey yazamadım. Bunun tek nedeni ise aslında tembellik.Smile Başka bir bahanenin arkasına sığınmıyorum.

Bu yazımızda ise yine paralel programlama konusundan devam ediyor olacağız ve Paralel olarak çalışan döngülerin nasıl sonlandırılabileceğini incelemeye çalışacağız. Bildiğimiz gibi paralel olmayan döngülerde Break metodunu kullanarak döngülerden çıkabiliyorduk. Ancak Paralel olarak çalışan döngülerde bu şekilde kullanım sözkonusu değil. Bunun nedeni ise aslında Paralel tarafta kullandığımız for döngüsünün aslında bir metot olması. Bu nedenle paralel döngüleri sonladırmak için ParallelLoopState tipi içerisinde bulunan Break ve Stop metotlarını kullanıyor olacağız. Bu iki metot arasında aslında anlatması ve anlaması biraz zor olan bir fark var. Bu farkı anlamak için isterseniz bir örnek yapalım ve daha detaylı bir şekilde inceleyelim.

İlk olarak ParallelLoopState tipine bakıyor olacağız. Parallel tipi içerisinde bulunan For ve Foreach metotlarına baktığımızda Action<int,ParallelLoopState> şeklinde iterasyon içerisinde işletilecek komutları alan aşırı yüklenmiş metotlar olduğunu görmekteyiz. Peki bu ParallelLoopState tipi ne işe yarıyor ? Bu tip aslında çalışma zamanı sırasında derleyici tarafından üretiliyor ve Paralel olarak işletilen döngü ile ilgili çeşitli işlemleri yapabilmemizi ve çeşitli durum bilgilerini almamızı sağlıyor. Bir de örneğimizde ConcurrentStack tipini kullanıyor olacağız. Bu tipte .Net Framework 4.0 ile beraber gelen Concurrent Collectionlar içerisinde yer almakta. Aslında bildiğimiz koleksiyonların paralel olarak çalışan versiyonları olarak özetleyebiliriz. Bu tipler ile ilgili detaylı olarak ilerleyen günlerde bir yazı yazıyor olacağım.

Örnek olarak ise elimizde 1 ile 1000 arasında integer değerleri tutan bir koleksiyon olacak ve paralel olarak bu koleksiyonu dolaşıp 500'den küçük değerleri elimizdeki ConcurrentStack içerisine ekleyeceğiz. Örnek kodumuz ise şu şekilde olacak.

static void Main()
{
   IEnumerable<int> sayilar = Enumerable.Range(1, 1000);
   ConcurrentStack<int> stack = new ConcurrentStack<int>();
   Parallel.For(0, sayilar.Count() - 1, (p, loopState) =>
           {
              Console.WriteLine("{0} sayısı kontrol ediliyor",p);
              if (p > 500)
                 loopState.Stop();
              else
                 stack.Push(p);
              if (loopState.IsStopped)
                 Console.WriteLine("{0} sayısını kontrol eden durduruldu", p);
           });
   Console.WriteLine("Stackte {0} eleman var",stack.Count);
}

Stop komutu yaratılmış olan ve yaratılacak tüm iterasyonlara mümkün olan en uygun  zamanda işlemlerini sonlandırma mesajı göndermektedir. Ayrıca ParallelLoopState tipi içerisindeki IsStopped propertysi ile de o anda çalışan iterasyona stop mesajı gönderilip gönderilmediğini de algılayabiliyoruz.

Örnek bir ekran görüntüsü ise şu şekilde

Yukarıda gördüğümüz gibi 998 sayısını kontrol ederken stop çağrısı gönderilmekte. Böylece yaratılan iterasyonlara işlemlerini en kısa sürede ve uygun zamanda bitirmeleri gerektiği çağrısı gönderilmiştir. Ancak yine de gördüğümüz gibi bazı iterasyonlar anında sonlanmamış çalışmalarına devam etmiştir. Böylece stacke 4 eleman eklenmiş durumda. Önemli olan bir nokta da diğer elemanları kontrol etmek için yaratılan paralel iterasyonların hiç işlem yapmadan sonlanmış olmaları.

Şimdi gelelim aynı örneği Break metodunu kullanarak yapmaya.

static void Main()
{
   IEnumerable<int> sayilar = Enumerable.Range(1, 1000);
   ConcurrentStack<int> stack = new ConcurrentStack<int>();
   Parallel.For(0, sayilar.Count() - 1, (p, loopState) =>
           {
              Console.WriteLine("{0} sayısı kontrol ediliyor",p);
              if (p > 500)
                 loopState.Break();
              else
                 stack.Push(p);
              if (loopState.IsStopped)
                 Console.WriteLine("{0} sayısını kontrol eden durduruldu", p);
           });
   Console.WriteLine("Stackte {0} eleman var",stack.Count);
}

Break metodu ise mesajın gönderildiği iterasyondan önce yaratılan iterasyonların işlemlerini bitirdikten sonra sonlanmaları mesajını vermektedir.

Burda da gördüğümüz gibi bu sefer stack içerisinde 501 eleman bulunmakta. Yani yaratılan iterasyonlar işlemlerine devam ettiler ve bu süreçte de gerekli kontroller yapılarak stack'e 501 eleman eklenmiş durumda.

Bir yazımızında daha sonuna geldik. Ancak yazıdan da anlayabileceğiniz üzere Break ve Stop metotları arasındaki fark biraz karmaşık. Bu nedenle farklı kaynaklardan da aynı konuda yazıları okumakta fayda var. Örneğin MSDN üzerinde aynı konu ile ilgili yazılmış olan yazıyı buradan inceleyebilirsiniz.

Görüşmek Üzere,



.NET Framework 4.0 - Paralel Programlama Yenilikleri

Merhaba Arkadaşlar,

Bu yazımızda günümüzde oldukça popüler olan ve .NET Framework 4.0 ile beraber oldukça kuvvetli bir şekilde hayatımıza girecek olan Paralel Programlama paradigmasından bahsediyor olacağım ve soyut bir şekilde .NET Framework 4.0 ile ne gibi yenilikler gelecek bunları anlatmaya çalışacağım. Günümüzde baktığımızda satın aldığımız tüm bilgisayarların artık birden fazla çekirdeği bulunmakta. Masaüstü ve dizüstü bilgisayarlara baktığımız zaman 2 yada 4 çekirdekli işlemcilerle sunucu bilgisayarlara baktığımızda ise 8,16 çekirdekli hatta daha fazla  sayıda çekirdeğe sahip olan işlemcilerle karşılaşmaktayız.

Bilgisayarlar üzerinde birden fazla çekirdek olmasından dolayı artık aynı anda işlemci üzerinde birden fazla thread çalıştırma imkanına sahibiz. Ancak günümüzde kullandığımız programlama yapısıyla maalesef bilgisayar üzerinde bulunan tüm çekirdekleri etkin bir şekilde kullanamıyoruz. Hatta çoğu zaman yazdığımız uygulamalar tek bir thread üzerinde çalışmakta. Ayrıca yazdığımız uygulamalar zaman ilerledikçe bilgisayardaki çekirdek sayısı artsa bile bazı durumlarda tek bir thread üzerinde çalıştığından dolayı daha da yavaş çalışabilmekte.

Tüm bu nedenlerden dolayı .NET Framework 4.0'ın temalarından biri de Paralel Programlama. .NET Framework 4.0 ile beraber artık uygulamalarımızı eş zamanlı olarak birden fazla thread üzerinden çok daha hızlı ve verimli bir şekilde çalıştırabileceğiz. Peki .NET Framework 4.0 ile beraber Paralel Programlama çerçevesi içerisinde neler bizleri bekliyor.

  • Çekirdek Yenilikleri
  • Concurrent Koleksiyonlar
  • Task Parallel Library
  • Parallel LINQ
  • Visual Studio 2010 Paralel Yenilikleri
  • Çekirdek Yenilikleri
.NET Framework'ün derinliklerinde ( Garbage Collector gibi... ) Paralel Programlama ile ilgili birtakım yenilikler bulunmakta. Bu yeniliklerden ilerleyen zamanlarda bahsediyor olacağım.
  • Concurrent Koleksiyonlar
Paralel Programlama ile beraber artık paralel şekilde çalışacak olan arka planda tüm kilitleme işlemlerini otomatik olarak yapacak olan ve eşzamanlı çalışma kabiliyetine sahip olan koleksiyonlar framework içerisine dahil edilmiş durumda.
  • Task Parallel Library
Paralelliği en soyut şekilde gerçekleştirmemizi sağlayan Task Paralel Library, .NET Framework 4.0 içerisine katılmış durumda. Bu kütüphane ile beraber artık işlemlerimizi paralel bir şekilde altyapı ile ilgilenmeden dekleratif bir şekilde yapabilmekteyiz.
  • Paralel LINQ
.NET Framework 4.0 ile beraber artık LINQ to Objects sorgularımızı da Paralel olarak çalıştırabilmekteyiz.
  • Visual Studio 2010 Paralel Yenilikleri
Framework'te Paralel Programlama yenilikleriyle beraber Visual Studio 2010 içerisinde de Paralel Programlama ile ilgili pek çok yenilik bulunmakta. Paralel olarak çalışan threadlerin gözlenmesi, yeni profiling ekranları gibi pek çok yenilik Visual Studio 2010 ile beraber hayatımıza girmiş olacak. Özellikle Visual Studio 2010 Paralel Yeniliklerini görsel dersler şeklinde paylaşıyor olacağım.
 
Evet arkadaşlar gördüğünüz gibi .NET Framework 4.0 ile beraber dinamik tarafın dışında Paralel Programlama ile ilgili de pekçok yenilik gelmekte. Bu yenilikler ile ilgili yazılarımı da blogtan paylaşıyor olacağım.
 
Takipte Kalın Smile
Görüşmek Üzere,