arşiv

‘C#’ kategorisi için arşiv

Prime Factors Kata C# – Kod Kata

Pazar, 28 Tem 2013 yorum yok

Prime Factors Türkçe olarak Asal Çarpanlar şeklinde tercüme edilebilir. Prime Factors Kata’sını geliştirirken bir sayının asal çarpanlarının bulunması hedeflenmektedir. Bir sayının asal çarpanları bulunup liste şeklinde kullanıcıya verilmektedir. Aşağıdaki listede örnek olarak verilen sayıların çarpanları listelenmiştir.

Prime Factors (Asal Çarpanlar)

1 => [ ]
2 => [2]
3 => [3]
4 => [2,2]
5 => [5]
6 => [2,3]
7 => [7]
8 => [2,2,2]
9 => [3,3]
10 => [2,5]
24 => [2,2,2,3]

Categories: C#, Kod Kata Tags: ,

ASP.NET MVC Projesinde Dependency Injection Uygulanması

Pazar, 30 Haz 2013 8 yorum

Daha önce hazırladığım Dependency Injection Tasarım Deseni başlıklı yazıda, yazılım tasarımında sınıfların birbirine olan bağımlılıklarını esnetmeyi ve bağımlılıkların sınıf dışından enjekte edilmesini incelemiştik. Bu yazımızda ise konunun bir örnek uygulaması niteliğinde olan ASP.NET MVC uygulamalarında Dependency Injection uygulamasını inceleyeceğiz.

Bu yazımızdaki örnek uygulamamızı geliştirirken, soyut sınıflar ile somut sınıflar arasındaki bağlantıyı kurarken ve somut nesne oluştururken IoC Container denen yardımcı kütüphanelerden faydalanacağız. Yine bu örnek uygulamada IoC Container kütüphanesi ile  somut nesnelerin kullanıcı(client) sınıf içerisinde “new” anahtar sözcüğü ile oluşturmak yerine, IoC Container tarafından oluşturulduğunu göreceğiz.

Örnek Uygulama

Örnek uygulamamızda ASP.NET MVC controller sınıfına Dependency Injection tarasım deseni ile bağımlılıkları dışarıdan soyut nesneler(interface veya abstract) yardımıyla vererek uygulamayı çalıştırmayı deneyeceğiz.

Öncelikle Controller sınıfımızı tanımlayarak işe başlayabiliriz.

public class HomeController : Controller
{
    private readonly IProductService productService;

    public HomeController(IProductService productService)
    {
        this.productService = productService;
    }
    public JsonResult Index()
    {
        IEnumerable<Product> products = productService.GetBestSellers();

        return Json(products, JsonRequestBehavior.AllowGet);
    }

}

Gördüğünüz üzere, Controller sınıfında hiç bir şekilde nesne oluşturma işlemi gerçekleştirilmiyor. Dışarıda oluşturulan ProductService nesneleri Controller sınıfı içerisine enjekte ediliyor. IProductService tipi bir interface’dir. Bu interface tipini uygulayan sınıflar HomeController içerisinde çalışabilir. IProductController tipindeki nesneler constructor metodu yardımıyla sınıf içerisine alınıyor.

ASP.NET MVC Controller sınıflarının varsayılan(default) halleri çalışma zamanında parametresiz constructor metodlarını ararlar. Bu tanımlama ASP.NET DefaultControllerFactory içerisinde belirlenmiştir. ASP.NET MVC open source olduğundan isteyen MVC kodlarını açıp inceleyebilir. Biz constructor seviyesinde injection işlemi yapacağımızdan DefaultControllerFactory sınıfının GetControllerInstence metodunu ezerek yani yeniden tanımlayarak istediğimiz formata getirmeliyiz.

Bu tanımlamayı yapmadan önce uygulamada kullanacağımız NInject kütüphanesinden bir iki cümle ile bahsedelim. Ninject kitüphanesi bir konteynır olarak çalışır. Soyut nesneler ile somut nesnelerin eşleştirildiği ve istendiğinde somut nesnelerin(new) oluşturulduğu kütüphanelerdir.

public class NinjectControllerFactory : DefaultControllerFactory
{
     private readonly IKernel kernel;

     public NinjectControllerFactory()
     {
         kernel = new StandardKernel(new NinjectBindingModule());
     }

     protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
     {
         return controllerType == null ? null : (IController)kernel.Get(controllerType);
     }
}

public class NinjectBindingModule : NinjectModule
{
     public override void Load()
     {
         Kernel.Bind<IProductService>().To<ProductService>();
     }
}

Burada tanımlanan kod parçasında anlatılmak istenen şey DefaultControllerFactory metodunun Controller Instance oluştururken karşılaşacağı parametresiz constructor metodu oluşturma hatasını engellemektir. Ninject aracılığı ile Controller tipine göre bir nesne oluşturuyor. Örneğin HomeController için controllerType HomeController olacaktır ve constructor metodunda IProductService interface tipini parametre olarak alacaktır. HomeController tipinde bir nesne oluştururken constructor metodda karşılaştığı parametreyi Kernel bildiği için hemen nesnesini oluşturarak Controller sınıfına vermektedir.

Artık Global.asax sınıfı içerisine bu yapıyı tanıtma işlemi kaldı. Onu da şu şeklide gerçekleştirebiliriz.

public class MvcApplication : System.Web.HttpApplication
{
     protected void Application_Start()
     {
        //diğer tanımlamalar...

        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
     }
}

Uygulamayı çalıştırdığımızda çıktı olarak şu şekilde bir JSON veri dönecek:

[{"Name":"Notebook"},{"Name":"PC"}]

Örnek uygulamanın kodlarını SkyDrive üzerinden paylaştım. Bir sonraki yazıda görüşmek üzere.

Kaynak Kodlar: Burada

Operatörlerin Aşırı Yüklenmesi (Operator Overloading C#)

Pazartesi, 27 May 2013 yorum yok

Bu yazımızın konusu geliştiriciler tarafından oluşturulan sınıflar veya yapılar(struct) üzerinde operatörlerin aşırı yüklenmesidir. Operatörlerin aşırı yüklenmesi, bir operatörün bir nesne için bizim istediğimiz şekliyle çalışabilecek hale getirilmesidir diyebiliriz. Örneğin bir (+) operatörü varsayılan olarak iki sayının toplamı bulmak için veya iki ayrı metnin birleştirilmesi için kullanılır. Bazen yazdığımız sınıflara ait nesneler üzerinde operatörleri kullanmak isteriz. Bu durumda sınıfımızda bir takım değişiklikler yapmak zorundayız.

Bir örnek uygulama üzerinden operatörlerin aşırı yüklenmesini incelemeye çalışalım. Örneğimizin senaryosunda aşırı yükleme işlemini uygulamak için Price adında bir sınıf oluşturup bu sınıfa Cost adında bir property ekleyelim. İki farklı Price nesnesi üzerinden (+) gibi operatörleri aşırı yüklemeye çalışalım.

public class Price
{
      public double Cost { get; set; }

      public static double operator +(Price e1, Price e2)
      {
          return e1.Cost + e2.Cost;
      }
}

En basit gösterimi ile Price sınıfında (+) operatörünün aşırı yüklenmiş halini görüyoruz. Bir operatörün aşırı yüklenmesi bir takım kurallar içermektedir. Bunlar:

  • Operatör fonksiyonu static olmalıdır.
  • Operatör fonksiyonu, aşırı yüklenecek operatörün anahtar kelimesini belirtmelidir.
  • Operatör fonksiyonunun parametreleri, işlenecek tiplerdir.
  • Operatör fonksiyonunun geri dönüş türü belirlenmelidir.

Bu sınıf için (+) operatörünün kullanımı, şu şeklide opacaktır.

 Price p1 = new Price { Cost = 40.10 };
 Price p2 = new Price { Cost = 40.10 };

 var sum = p1 + p2; // sum = 80.20

Artık (+) operatörü, Price nesneleri için bir işleve sahip. Bu işlev, Price nesnesinin Cost parametrelerinin toplamını almasıdır.

Eğer aşırı yükleme işlemini yapmasaydık ve (+) operatörünü aynı şekliyle Price nesnesinde kullanmaya çalışsaydık şu şekilde bir hata alacaktık: “Operator ‘+’ cannot be applied to operands of type ‘OperatorOverrideApp.Price’ and ‘OperatorOverrideApp.Price

Price nesnesine int, double, float gibi built-in tipler ile toplamak istersek sadece işlenen argümanlarda şu şeklide bir değişiklik gerçekleştirmek yeterli olacaktır.

public static double operator +(Price e1, double e2)
{
     return e1.Cost + e2;
}

Bu şekilde, kendi sınıflarımızda operatörlerin aşırı yüklenmesi işlemini incelemeye çalıştık. Örnek uygulamaya ait kodlara buradan ulaşabilirsiniz.

Categories: C# Tags: ,

Mapping İşlemleri ve Automapper Performans Testleri

Cuma, 10 May 2013 4 yorum

Programlama dünyasında nesneler arası aktarım yapılması amacıyla geliştirilmiş olan Automapper kütüphanesi üzerinde yaptığım performans sonuçlarını yazmak istedim.

C# kodu ile geliştirdiğim örnek uygulama üzerinde sırsıyla 10, 100, 1000, 10000 ve 100000 kayıt üzerinde üç farklı şekilde Mapping işlemi yapmayı denedim.

Örnek senaryoda Product adında bir sınıf düşünün. Bu sınıfın 10 farklı property üyesi olsun. Ben bu 10 üyeli Product tipini taşımak istemediğimden ProductSummary adında 2 üyesi olan bir sınıf oluşturuyorum.

public class Product
{
   public int id { get; set; }
   public string Name { get; set; }
   public string Color { get; set; }
   public string Model { get; set; }
   public string Size { get; set; }
   ...
   ..
   .
}

public class ProductSummary
{
   public int id { get; set; }
   public string Name { get; set; }
}

Doğal olarak Product nesnesini bir şekilde ProductSummary nesnesine aktarmalıyım. Bu işlemi Automapper kütüphanesi ile ve manuel olarak denediğimde ve süreleri milisaniye cinsinden ölçtüğümde ilginç rakamlarla karşılaştım.

List<Product> products = new List<Product>();

Kayıt sayısı sırayla 10, 100, 1000, 10000 ve 100000 olarak değiştiriliyor ve her seferinde Mapping işlemi deneniyor.

Mapping-1 (Yöntem-1 Foreach döngüsüyle tek tek Mapping işlemi)

foreach (var product in products)
{
    ProductSummary summary = Mapper.Map<Product, ProductSummary>(product);
}

10             kayıt için süre    121 milisecond
100          kayıt için süre    127 milisecond
1000       kayıt için süre    155 milisecond
10000     kayıt için süre    498 milisecond
100000  kayıt için süre    5298 milisecond

Mapping-2 (Yöntem-2 Kolleksiyonu direk mapping işlemine sokmak)

IEnumerable<ProductSummary> summary = Mapper.Map<IEnumerable<Product>,
                                    IEnumerable<ProductSummary>>(products);

10           kayıt için süre    133 milisecond
100         kayıt için süre    126 milisecond
1000      kayıt için süre    221 milisecond
10000    kayıt için süre    152 milisecond
100000  kayıt için süre    339 milisecond

Mapping-3 (Yöntem-3 Automapper kullanmadan manuel olarak nesneleri oluşturmak)

foreach (var product in products)
{
    ProductSummary summary = new ProductSummary();
    summary.id = product.Id;
    summary.Name = product.Name;
}

10           kayıt için süre    1 milisecond
100         kayıt için süre    1 milisecond
1000      kayıt için süre    2 milisecond
10000    kayıt için süre    4 milisecond
100000  kayıt için süre    42 milisecond

Mapping-4 (Yöntem-4 LINQ Extension metodlar yarımıyla mapping işleminin gerçekleştirilmesi)

var data = products.Select(summary=>
                 new ProductSummary{
                    Id= summary.Id,
                    Name = summary.Name
});

10           kayıt için süre    1 milisecond
100         kayıt için süre    1 milisecond
1000      kayıt için süre    1 milisecond
10000    kayıt için süre    1 milisecond
100000  kayıt için süre    1 milisecond

Mapping Raporları

Mapping Raporları

Bu sonuçlara göre Automapper kütüphanesi Mapping işlemlerinde biraz yavaş kaldığı aşikardır. En hızlı yöntemin LINQ Extension metodları yardımıyla Mapping işleminin olduğu ortaya çıkmaktadır. 100.000 kaydın mapping işlemine tabi tutulması 1 milisaniye sürmektedir. Aynı işlem Automapper kütüphanesi ile 5000 milisaniyeden fazla sürmektedir. Yani zaman probleminin olmadığı uygulamalarda Automapper kullanılabilir. Ancak işlemleri uzun sürede bitirmesi amacıyla yazılmış bir uygulama şu ana kadar hiç görmedim.

Bir sonraki yazıda görüşmek dileğiyle.

Categories: C# Tags: ,

LINQ sorgularında Karşılaşılan NotSuportedException

Pazartesi, 11 Mar 2013 yorum yok

LINQ sorguları kolleksiyon temelli yapılarda sorgulamalar ve seçimler yapmak için bize imkan sağlar. Döngülerle diziler içerisinde boğuşmadan istediğimiz formatta sonuç almamıza yardımcı olurlar.

Bazen ihtiyaçlarımız doğrultusunda bir tipte oluşturulmuş kolleksiyon içerisinden başka bir tipte seçimler yapmak durumunda kalabiliriz. Bu noktada kurtarıcı olarak yine LINQ sorgularından faydalanabilmekteyiz.

Örneğin bir ürüne ait bilgileri tutan “Product” tipine ait kolleksiyon içerisinden bize lazım olan ürün başlıklarını seçip alacağımız “ProductTitle” tipli kolleksiyon oluşturma gereği duyduğumuz bir senaryo düşünebiliriz. Bu durumda tüm “Product” nesnesini taşımak yerine ürün adlarını barındıran “ProductTitle” adlı tipi tercih etmiş oluyoruz. Bu şekildeki bir seçimi yapmak için şu şekilde bir LINQ sorgusu yazabiliriz.

     var productTitles = db.Products.Select(p => new ProductTitle{
           Id = p.Id,
           Name = p.Name
     });

Ancak bazı durumlarda seçim yaptığımız tip özellikleri, Property ataması ile değil de bir kurucu metod (Constructor) ile tipe enjekte edileceği durumu senaryo edecek olursak nasıl bir durumla karşılaşacağımıza bakalım.

     StoreEntities db = new StoreEntities();

     var productTitles = db.Products.Select(p => new ProductTitle(p));

Yukarıdaki sorguyu, eğer bir Entity Framework modeli üzerinde çalıştıracak olursak karşımıza şu şekilde bir hata mesajı çıkacaktır.

Unhandled Exception: System.NotSupportedException: Only parameterless constructors and initializers are supported in LINQ to Entities.

LINQ provider, ProductTitles tipinini kurucu metodunda nasıl bir kodun çalıştırılacağından habersizdir. Entity Framework ise LINQ sorgularını olduğu gibi T-SQL sorgularına dönüştürmeye çalışır. Fakat sorgu içerisindeki bir tipin(ProductTitle) kurucu metodunda neler olup bittiğini bilmediğinden işlem durdurulur ve hata raporu oluşturulur.

Bu durumun çözüm yolu ise Entity Framework tarafından IQueryable olarak sunulan kolleksiyonu AsEnumerable extension metodu yardımıyla IEnumerable olarak değiştirmektir.

     StoreEntities db = new StoreEntities();

     var productTitles = db.Products
                           .AsEnumerable()
                           .Select(p => new ProductTitle(p));

Burada sorguda AsEnumerable() extension metodu önce veriyi çekiyor. Ardından seçilen kolleksiyon içerisinden seçim işlemi yapılıyor. Bu sayede hata giderilmiş oluyor. Entity Framework sorgularının IQueryable ve IEnumerable karşısındaki davranışları ve T-SQL dönüşümünü incelemek için Burak Selim Şenyurt’un buradaki ipucundan faydalanabilirsiniz.

Bir sonraki yazıda görüşmek dileğiyle.

C# if İfadesi Yerine Dictionary Kullanmak

Cumartesi, 23 Şub 2013 1 yorum

Proje geliştirirken karşılaştığımız sorunlardan biri de “if” kullanımı sırasındaki kod fazlalığından dolayı kodun okunabilirliğinin azalmasıdır. Buna örnek olarak aşağıdaki gibi bir örnek verebiliriz. Bu örnekte matematiksel bir işleme karar vermek için belirlenmiş bir dizi “if” sınaması gerçekleştirilmektedir.


public class OperationData
{
    public int X { get; set; }
    public int Y { get; set; }
    public string Operation { get; set; }
}

public ActionResult Save(OperationData operationData)
{
    if (operationData.Operation == "+")
    {
        // Sum
    }
    else if (operationData.Operation == "-")
    {
       // Sub
    }
    else if (operationData.Operation == "*")
    {
       // Mul
    }
    else if (operationData.Operation == "/")
    {
       // Div
    }
    throw new Exception();
}

}

Bunun yerine daha temiz bir tasarım fikri geliştirmek mümkündür. Bu tasarım fikrine okuduğum kitaplarda ve makalelerde de rastlıyorum. Fikri geliştiren hakkında kaynak göstermek isterdim ama elimde kesin bir kaynağa sahip olmadığımdan net bir kaynak gösteremiyorum. Şimdilik sadece fikri sunana teşekkür etmekle yetiniyorum.


public class MathController
{
    readonly Dictionary<string,Func<OperationData,ActionResult>> handleAction =
       new Dictionary<string, Func<OperationData, ActionResult>>
         {
            {"+", SumAction},
            {"-", SumAction},
            {"*", SumAction},
            {"/", SumAction},
         };

   public ActionResult Save(OperationData operationData)
   {
       if (handleAction.ContainsKey(operationData.Operation))
       {
          return handleAction[operationData.Operation](operationData);
       }
       throw new Exception();
   }

}

Yukarıdaki yaklaşım daha temiz ve okunabilirlik bakımından gayet iyi bir şeçim olacaktır. handleAction kolleksiyonunu uygun operasyon bilgileriyle doldurarak istediğimiz işlemleri başarıyla gerçekleştirebiliriz.

Categories: C# Tags:

Bir “if” sınamasına farklı bakış

Salı, 29 Oca 2013 yorum yok

Kodalama yaparken karşılaştığım uzunca bir “if” sınaması için farklı bir yaklaşımı sunmak istedim.


public class OperationManager
{
     public IOperation GetOperation(string arg)
     {
        if (arg == "+" ||
            arg == "-" ||
            arg == "*" ||
            arg == "/")
        {
            return new MathOperation();
        }

        return new UnknownOperation();
    }
}

Yukarıdaki ifadenin alternatifi olarak aşağıdaki gibi bir çözüm üretebiliriz. Okunabilirliği arttıran bir yöntem gibi görünüyor. Kod satırları azaldığından göze daha hoş gelen bir yazım biçimi oldu.

public class OperationManager
{
     private readonly string[] operations = { "+", "-", "*", "/" };

     public IOperation GetOperation(string arg)
     {
         if (operations.Contains(arg))
         {
             return new MathOperation();
         }
         return new UnknownOperation();
     }
}
Categories: C# Tags:

async/await ile Asenkron Programlama

Salı, 22 Oca 2013 yorum yok

Asenkron işlmeler

Asenkron çalışma prensibi, yürütülen süreçlerin uzun sürmesinden dolayı, yürütülmesi gereken diğer süreçlerin beklemeden çalışmasına devam edebilmesi için geliştirilmiştir. Bazen wab ortamındaki bir kaynağa erişip istekte bulunmak ve cevap almak uzun sürebilmektedir. Senkron çalışma prensibinde örneğin bir web kaynağından cevap gelene kadar sıradaki işlem yürütülmez.  Ancak asenkron süreçlerde uzun sürebilecek işlemler farklı bir görev olarak tanımlanarak çalıştırılır ve sıradaki işleme geçilir. Böylece uzun süren işlemler arka planda farklı görevler olarak çalışmaya devam eder.

Asenkron progamlama genellikle Windows Forms, WPF ve Silverlgiht gibi uygulamalarda, uzun süren işlemlerde form arayüzlerinin kullanıcıya cevap verememesi gibi sorunların çözümü için kullanılabilir. Grafik arayüzlü(UI) uygulamalarda tüm grafiksel işlemler tek bir thread üzerinden yürütülür. Örneğin veritabanından yüklüce bir veriyi çekip Datagrid gibi bir nesneye yüklemeye çalıştığımızda thread önce veriyi almalıdır ki aynı thread daha sonra aldığı veriyi Datagrid nesnesine bağlayabilsin. Tabi bu süre içerisinde Formu sürüklemek yada Form üzerindeki diğer bileşenlere ulaşmak da engellenmiştir.

Diğer taraftan dosya işlemleri, resim işlemleri, WCF, soket uygulamaları gibi işlemler de asenkron süreçlerin işletilmesine ihtiyaç duyulabilecek işlemlerdir.

.Net Framework 4.5 öncesinde asenkron API’leri şu sırayla gelişmiştir:

  1. Asynchronous Programming Model (APM)
  2. Event based Asynchronous Pattern(EAP)
  3. Task Parallel Library(TPL) tabanlı, Task-based Asynchronous Pattern(TAP).

Bu yazıda üzerinde durduğumuz konu da TAP modeli ile asenkron programlama seçeneğidir.

TAP modeli ile asenkron programlamanın tam merkezine oturmuş olan “async” ve “await” anahtar kelimelerini inceleyerek devam edelim. Bu iki sihirli kelime, senkron çalışan bir süreci asenkron hale çevirebilmek için kullanılırlar.

Anahtar kelimelerin (async/await) kullanımı

“async” anahtar kelimesi, kullanıldığı metod içerisinde “await” anahtar kelimesinin etkinleştirilmesi  için kullanılır. “async” anahtar kelimesinin kullnaılmadığı metodlarda “await” anahtar kelimesi çalışmaz. Yani “async” anahtar kelimesinin kullanıldığı metodu asenkron hale çeviren bir sihiri yoktur.

Async kullanılan metodlar tıpkı diğer metodlar gibi çalıştırılır. Her hangi bir farkı yoktur. Ta ki derleyici “await” ile karşılaşana kadar işlemler senkron olarak işletilir. İşlemler Thread pool içerisinde bir thread ile yürütülür. “async” ve “await” kelimelerinin kullanımı aşağıdaki gibidir.

private async void download(string url)
{
    var down = await Downloader.DownloadString(url);
}

Yeri gelmişken Thread akışını şu şekilde inceleyebiliriz.

Main metod ve download metodu içerisinde “await” kelimesine kadar olan işlemler Thread 1 ile gerçekleştirilmektedir. “await” kelimesinden sonra işlemler Thread Pool içerisinden başka bir thread olan Thread 3 ile yürütülmüştür.

“await” kelimesinin kullanıldığı yerdeki işlem asenkron şekilde yürütülür. “await” kelimesi matematiksel operatorler gibi düşünülebilir. “await” anahtar kelimesi sadece await edilebilir metodlar ile kullanılabilir. Her metodun önüne “await” klimesini kullanamayız, sadece await için uygun metodlar için bu kelimeyi kullanabiliriz.

Yukarıdaki örnekte System.Threading.Tasks.Task tipine ait Run metodu (awaitable) şeklinde düzenlenmiştir. Artık DownloadStringTaskAsync metodu “await” anahtar kelimesi ile kullanılabilir.

Asenkron metodlar için Unit Test

Yukarıda yazılan örnek metod için bir de unit test yazacak olursak.

Eğer “async/await” kelimelerini unit testler yazarken de kullanmalıyız. Aksi taktirde compailer, Downloader.DownloadString() metodu için bir awaitable Task tipi döndüreceğinden metodumuz testten geçemeyecektir.

Bu yazıda kullanılan örnek kodları barındıran uygulamaya SkyDrive üzerinden bu linkten ulaşabilirsiniz.

Tekrar görüşmek dileğiyle.

Categories: C# Tags: , , , ,

Task Parallel Library TLP C#

Pazartesi, 24 Ara 2012 2 yorum

İşlemci dünyasının gelişimi ve değişimi, işlemcilerin çekirdek sayısı transistör sayısını ne zaman yakalar diye düşündürüyor insana. Bilgisayarlarımızın işlemcileri geliştirkçe programlarımızın daha da hızlandığı aşikardır. Ancak programların hızlanması sadece bilgisayarların fiziksel özellikleriyle alakalı bir durum değildir. Diğer taraftan yazılımların da işlemcileri iyi kullanabilecek şekilde hazırlanmaları gerekmektedir.

İşlemci çekirdekleri arttıkça uzun sürebilecek işlemleri parçalar halinde bölüştürerek işlemcileri tam kapasitede kullanabilmek işimizi daha çok hızlandıracaka ve kolaylaştıracaktır.

Bu yazımızda da paralel işlemlerin alt yapısını oluşturan Task Parallel Library (TLP) konusunu incelemeye çalışacağız.

TPL kavramını .Net Framework 4.0 ile tanımaya başlıyoruz. Bu yapı, işlemlerin paralel bir şekilde yürütülmesini sağlamak amacıyla oluşturulmuştur. TLP’ye ait tipler System.Threading ve System.Threading.Tasks isim alanında bulunmaktadır.

TLP altyapısı aslında süreçlere, görevler olarak bakar ve tıpki insanların görevleri yütebildiği mantıkla organize edilebilmektedir.

  • Yeni görevler oluşturmak, bu görevleri başlatmak, duraklatmak ve sonlandırmak mümkündür.
  • Bir görevin bittiği yerden başka bir görevi başlatmak mümkündür.
  • Başarıyla yerine getirilen görevlerin sonucunda değerler döndürmek mümkündür.
  • Bir görev kendi içinde alt görevler başlatabilir.
  • Görevler aynı veya farklı thread’ler tarafından yerine getirilebilirler.

Task(görev), başarılı bir şeklide tamamlanmasını istediğimiz bir süreçtir. Süreç T1 zamanında başlar ve T2 zamanında sonlanır.

Bu sürecin tamamlanma süresi, işlemcimizin özelliklerine ve kod yazım biçimlerine göre değişebilir.

Thread(ler) ise görevleri yerine getiren işçilerdir. Bu işçiler bir veya birden fazla olabilir. Her görev ayrı bir thread tarafından yürütülmek zorunda değildir. Bir thread birden fazla görevi yerine getirebilir. Ya da bir görev birden fazla thread(multithread) yardımıyla yapılabilmektedir.

Paralel programlama her problemin çözümü için hızlı bir çözüm olmayabilir. Paralel işlemler ile çözüme birden fazla thread yardımıyla yaklaşmış oluyoruz. Örneğin bir apartmanın en üst katına 2 tuğla çıkarmak için bört kişi çağırıp 1 tuğlayı iki kişiye tutturup çıkartmak mantıklı değildir ve maliyetlidir. Bu işi bir kişi gayet seri bir şeklide yapabilir ve uzucdur. Ancak 100 tuğla olduğunda bir kişi  bu işte çok yorulacağından maliyet fazladır. Bu durumda 100 tuğla için dört kişi çağırmak mantıklıdır ve maliyetleri azaltır.

Yazılımsal olarak gelişmiş bir örnek üzerinden giderek sonuca bakacak olursak, paralel ve seri işlemlerin farkını daha rahat görebiliriz.

Bu örneğimizin senaryosu istatistiksel bir formül olan Standart Normal Dağılım’ın matematiksel formülünü döngü yardımıyla farklı sürelerde hesaplatmaya çalışalım. Burada seçilen formülün Standat Normal Dağılım olmasının hiç bir önemi yoktur. İsterseniz x=2y formülünü de kullanabilirsiniz. Ben işlemler uzun sürmesi açısından bu formülü seçtim.

Formülümüz şu şekildedir:

Bu formülü for döngüsünü 10, 100, 1.000, 10.000, 30.000, … şeklinde farklı değerlere kadar seri ve paralel şeklide hesaplattırıp sonucu bir tabloda incelemeye çalışalım.

İşlemi öncelikler seri işlemler mantığı ile yaparak sonuca bakacak olursak, bir işlemin bitmeden diğerinin başlamadığını göreceğiz. Döngümüz işlemi 10000’e kadar 30 kez yapacak.

For döngüsü adımsal işlem gerçekleştirmekte ve  her döngüyü aynı thread gerçekleştirmektedir. Tüm işlemler 1 numaralı thread tarafından gerçekleştirilmektedir.

Şimdi de işlemi paralel olarak gerçekleştirelim. Aynı şeklide işlemi 10000’e kadar 30 kez yapacak.

Paralel işlemlerde kullandığımız tip System.Threading.Tasks.Parallel olacaktır. Paralel for kullanımı şeklideki gibi basitçe ifade edilmektedir ancak arka planda Task tipleri hemen görevleri bölüşürler.

Paralel işlemi incelediğimizde işlemin 4 farklı thread tarafından yapıldığını ve sürenin seri işlemlerden daha uzun sürdüğünü görmekteyiz. Ancak döngü sayısını arttırdığımızda şu şeklide bir tablo karşımıza çıkacaktır.

Döngü sayısı arttıkça paralel işlemlerin adımsal işlemlerden daha kısa sürede gerçekleştiğini görmekteyiz. Örneğimizde kritik sınır 30.000 olarak görünmektedir.

Senaryonun grafiksel ifadesinden de durumu bu şeklide inceleyebiliriz. Bu sistemi test ederken kullandığım laptop biraz eski olduğundan işlemler uzun sürdü. Intel Centrino Core 2 işlemci üzerinde sonuçları bu şeklide aldım. Farklı işlemcilerde sonuçları test edebilirsiniz. Yorumlara yazabilirseniz karşılaştırma yapabiliriz. Ancak benim deneme yaptığım işlemci modelinden kimsede kalmadığından emin gibiyim. Nesli tükendi artık.

Bir sonraki yazıda görüşmek dileğiyle.

C# Action ve Action ön tanımlı delege türü

Cumartesi, 22 Ara 2012 2 yorum

Delegeler ile ilgili yazımızda delegeleri tanımlamayı ve kullanmayı incelemiştik. Delegelerin metodları temsil ettiğini ve hangi tür metodların temsil edileceğini ise delegeler oluşturulurken belirlendiğini görmüştük.

Ardından Func<T> ile ilgili yazımızda önceden tanımlanmış delege türü olan Func<T> delege türlerini incelemiştik. Func<T> ile temsil edilen metodların mutlaka bir değer döndürdüğünü belirtimştik.

Bu yazımızda ise delegelerin bir başka özel ve ön tanımlı türü olan Action ve Action<T> delege türünü incelemeye çalışacağız.

Action ve Action<T> delege türleri .Net framework 3.5 ile gelen önceden tanımlanmış bir delege türüdür. Action<T> delege türleri Func<T> delegelerinin aksine hiçbir değer döndürmeyen metodları temsil etmektedir. Action delege türü hiçbir parametre almayan ve değer döndürmeyen metodları temsil eder. Adından da anlaşılacağı üzere bir fonksiyonu değil bir eylemi(action) temsil etmektedirler.

Action<T> delegeleri birden fazla parametre alabilecek metodları da temsil edebilecek şekilde tasarlanmıştır.

Action
Action <T>
Action <T1,T2 >
Action <T1,T2,T3>
Action <T1,T2,…,TN>

Her bir T, metodların alacağı parametrelerin tiplerini belirtmektedir. Func<T> gibi Action<T> kullandığımızda delege tanımlamamıza gerek kalmamaktadır.

Action delegelerinin lambda ifadeleriyle göterimini şu şekilde yapabiliriz:

Lambda ifadelerini kullanmadan metod çağırarak action metod kullanımını şu şeklide kullanabiliriz.

Action delege tipinin kullanımına genelde LINQ ifadeleri ile çalışırken rastlarız. LINQ extension metodlarından Action delege parametre alanlara, uygun metodu parametre olarak gönderebiliriz. Buna bir örnek olarak kişilerin bulunduğu bir listedeki isimlerin önüne “Mr” ön ekini getirerek yazdırmayı deneyebiliriz.

List<T> tipinin ForEach extension metodu, string bir parametre alan metodları temsil etmektedir. Listedeki isimlere tek tek “Mr” ön ekinin nasıl getirildiğini bu şekilde görebilmekteyiz.

Bir sonraki yazıda tekrar görüşmek üzere…

Categories: C# Tags: ,