.Net Framework'te Timer Class'ları Arasındaki Farklar

BilgeAdam’da öğrencilerime WinForms anlatırken, kafalarının çok karıştığı konulardan biri de Timer nesnesidir.

Daha doğrusu Timer nesneleridir demeliyim. Çünkü .Net Framework’te Timer ismine sahip tam 3 tane class var;

Özellikle Multi-Threaded uygulamalarda tercih edilir. Thread-safe olduğu için, birden çok thread’den erişilebilir.

System.Timers.Timer t = new System.Timers.Timer(5000); t.Elapsed += delegate(object _s, System.Timers.ElapsedEventArgs _e) { MessageBox.Show(“5 saniye geçti!..”); t.Stop(); }; t.Start();</pre> System.Threading.Timer

TimerCallBack delegate kullanarak, atanmış metodu çalıştırabilir. Metod, framework tarafından oluşturulan başka bir thread’de çalışır.

Timer nesnesi oluşturulduğu anda, çalışmaya başlar ve scope’tan çıkana kadar da çalışır. Durdurmak veya Başlatmak mümkün değildir.

Aşağıdaki örnekte 2.parametre (null), Callback method’a aktarılacak parametreyi gösterir.

3.parametre (1000), Timer nesnesi başlatılmadan önce beklenecek süreyi gösterir.

System.Threading.Timer t = new System.Threading.Timer(new System.Threading.TimerCallback(TimerTest), null, 1000, 5000);
private void TimerTest(object state)
{
    MessageBox.Show("5 saniye geçti!..");
}

System.Windows.Forms.Timer

Toolbox’ta gördüğümüz nesnedir. Windows Form ile aynı thread’de ve senkron olarak çalışır, böylece hiçbir UI operasyonunu bölmez.

System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 5000;
t.Start();
t.Tick += delegate(object _s, EventArgs _e)
{
    MessageBox.Show("5 saniye geçti!..");
    t.Stop();
};

Daha ayrıntılı bilgi için MSDN'deki şu makaleyi okuyabilirsiniz.

Kısa Sınav - 8

Database işlemleri gerçekleştiren bir web service yazıyorsunuz. Faturalama ihtiyaçlarından dolayı, her kullanıcının web service’i kullanma miktarını hesaplamanız gerekiyor.

Kullanıcıların web service’e her erişmesiyle değeri 1 artırılacak session değişkeni kullanmayı planlıyorsunuz.

Web Method içerisinde session değişkeni kullanmadan önce ne yapılmalı?

  • WebMethod attribute’üne TransactionOption = TransactionOption.Supported eklenmeli
  • web.config dosyasına <sessionState mode=”InProc” /> eklenmeli
  • WebMethod attribute’üne EnableSession = true eklenmeli
  • WebMethod attribute’üne CacheDuration = 20 eklenmeli Sorunun doğru cevabı için;

Doğru cevap; WebMethod attribute’üne EnableSession = true eklenmeli

EnableSession = true Web Method‘un session değişkenlerine erişebilmesini sağlar. Eğer bu parametreyi true yapmazsanız, session değişkenleri herzaman null (VB.NET’te Nothing) döndürecektir.

<sessionstate = “InProc” /> varsayılan konfigürasyondur. web.config dosyasında bu satırın bulunması session değşikenlerine erişim sağlamaz.

InProc değeri ile session değişkenlerinin, web sunucusunun hafızasında (Ram Bellek) bulundurulacağı belirtilmiş olur. Bu değeri SQL Server veya State Server değerleri ile değiştirebilirsiniz.

CacheDuration, cache mekanizmasını konfigüre eder, session ile alakalı değildir.

TransactionOption = TransactionOption.Supported, Transaction desteğini aktif hale getirir, session ile alakalı değildir.

Twitter Arama - Twitter Search

Bu yazımda, Twitter‘da arama yapan bir uygulama yazacağım. Siz de yazıyı baştan sona takip ederek, kendi twitter arama uygulamanızı yazabilirsiniz.

Hemen ekran tasarımımızı yapmakla işe başlayalım;

TwitterSearch_1

Sayfadaki elemanlar;

  • txtSonucAdet
  • txtSayfa
  • btnGeri
  • btnIleri
  • txtArama
  • btnArama
  • flowLayoutPanel1 Öncelikle Twitter Search API‘sini inceleyelim;

Search fonksiyonu geriye JSON formatında bilgi döndürüyor. Uygulamalarınızda JSON verilerini kullanabilmek için, şu sayfadan bulabileceğiniz JSON parser class’ını kullanabilirsiniz.

Şimdi, butonların (btnArama, btnGeri, btnIleri) Click olayını yazalım;

private void btnArama_Click(object sender, EventArgs e) { txtSayfa.Text = “1”; AramaYap(); }</pre>

private void btnGeri_Click(object sender, EventArgs e)
{
    int Sayfa = Convert.ToInt32(txtSayfa.Text) - 1;
    txtSayfa.Text = Sayfa < 1 ? "1" : Sayfa.ToString();
    AramaYap();
}
private void btnIleri_Click(object sender, EventArgs e)
{
    txtSayfa.Text = (Convert.ToInt32(txtSayfa.Text) + 1).ToString();
    AramaYap();
}

Butonların Click olaylarının kalbinde AramaYap() methodu yatıyor. Hemen yazalım;

private void AramaYap()
{
    flowLayoutPanel1.Controls.Clear();

    WebRequest wr = WebRequest.Create(string.Format("http://search.twitter.com/search.json?q={0}&rpp={1}&page={2}", txtArama.Text, txtSonucAdet.Value, txtSayfa.Text));
    Stream s = wr.GetResponse().GetResponseStream();
    StreamReader sr = new StreamReader(s);
    string Sonuc = sr.ReadToEnd();

    Hashtable hs = (Hashtable)JSON.JsonDecode(Sonuc);

    foreach (Hashtable oItem in (ArrayList)hs["results"])
        flowLayoutPanel1.Controls.Add(CreateTwitItem(oItem));
    }

AramaYap() method’unda ilk iş, flowLayoutPanel’i temizliyoruz.

Daha sonra, Twitter Search API‘den öğrendiğimiz gibi, http://search.twitter.com/search.json adresine uygun parametreler ile sorgu atıyoruz.

Gelen bilgi JSON formatında olduğu için, JSON parser class’ımızı kullanıyoruz (JSON.JsonDecode) ve sonuç bilgisini Hashtable formatına çeviriyoruz.

Hashtable‘ın results öğesi ArrayList formatındadır ve arama sonucunun herbir satırını ifade etmektedir. Bu yüzden basit bir foreach döngüsü ile flowLayoutPanel’e ekleme yapıyoruz.

flowLayoutPanel’e ekleyeceğimiz her nesne CreateTwitItem fonksiyonunda oluşturuluyor;

private Panel CreateTwitItem(Hashtable TwitItem)
{
    Panel p = new Panel();

    string from_user = TwitItem["from_user"] != null ? TwitItem["from_user"].ToString() : "";
    string to_user = TwitItem["to_user"] != null ? TwitItem["to_user"].ToString() : "";
    string text = TwitItem["text"] != null ? TwitItem["text"].ToString() : "";
    string profile_image_url = TwitItem["profile_image_url"] != null ? TwitItem["profile_image_url"].ToString() : "";
    string tweet_id = TwitItem["id"] != null ? TwitItem["id"].ToString() : "";
    DateTime created_at = TwitItem["created_at"] != null ? DateTime.Parse(TwitItem["created_at"].ToString()) : DateTime.Now;
    string twitter_url = string.Format("http://twitter.com/{0}/statuses/{1}", from_user, tweet_id);

    p.Size = new Size(flowLayoutPanel1.Width - 23, 60);
    p.BorderStyle = BorderStyle.FixedSingle;

    PictureBox pb = new PictureBox();
    pb.Dock = DockStyle.Left;
    pb.Size = new Size(50, 50);
    pb.BorderStyle = BorderStyle.FixedSingle;
    pb.Load(profile_image_url);

    Label l1 = new Label();
    l1.AutoSize = false;
    l1.Location = new Point(pb.Width, 2);
    l1.Size = new Size(300, 15);
    l1.Text = string.Format("@{0} -> @{1} ({2} {3})", from_user, to_user, created_at.ToShortDateString(), created_at.ToShortTimeString());

    Label l2 = new Label();
    l2.AutoSize = false;
    l2.Location = new Point(pb.Width, 19);
    l2.Size = new Size(flowLayoutPanel1.Width - pb.Width - 20, 38);
    l2.Text = text;

    LinkLabel l3 = new LinkLabel();
    l3.Text = "Sayfaya Git";
    l3.Location = new Point(flowLayoutPanel1.Width - (l3.Width - 10), 2);
    l3.Click += delegate { Process.Start(twitter_url); };

    p.Controls.Add(pb);
    p.Controls.Add(l1);
    p.Controls.Add(l2);
    p.Controls.Add(l3);

    p.MouseEnter += delegate { p.BackColor = Color.LightYellow; };
    pb.MouseEnter += delegate { p.BackColor = Color.LightYellow; };
    l1.MouseEnter += delegate { p.BackColor = Color.LightYellow; };
    l2.MouseEnter += delegate { p.BackColor = Color.LightYellow; };
    p.MouseLeave += delegate { p.BackColor = SystemColors.Control; };
    l2.MouseLeave += delegate { p.BackColor = SystemColors.Control; };

    return p;
}

*CreateTwitItem()* fonksiyonunda bir **Panel** nesnesi oluşturup, içerisine bir picturebox, iki label, bir linklabel ekliyoruz ve geriye Panel nesnesini döndürüyoruz. Böylece foreach'in her adımında flowLayoutPanel'e yeni Panel nesnesi ekleniyor.

Uygulamamızı çalıştırıyoruz ve işte örnek ekran görüntüsü;

![TwitterSearch_2](/assets/uploads/2009/12/TwitterSearch_2.png "TwitterSearch_2")

İsterseniz uygulamanın kodlarını buradan indirebilirsiniz.

Kısa Sınav - 7

WCF’teki Channel Model‘e göre, aşağıdaki Channel’lardan hangisi Channel Stack’te en alt katmandır?

  • Application
  • Protocol
  • Network Interface
  • Transport Sorunun doğru cevabı için; Doğru cevap: Transport Layer.

Transport Layer, mesajların alıp-gönderilmesinden sorumludur ve Channel Stack’te ent alt seviyededir.

Transport Layer’ın üstünde Protocol Layer yeralır ve onun üstünde de Application vardır.

Network Interface Layer, sadece Stack bir protokolle ilişkilendirildiğinde kullanılabilir. (Mesela TCP)

Normal For ve Paralel For Döngülerinin Karşılaştırması

Yazdığımız uygulamaların olmazsa-olmaz’larından birisi de, for döngüleridir.

Ne kadar basit’te olsa, ne kadar karmaşık’ta olsa, mutlaka uygulamalarımızda for döngülerine ihtiyaç duyarız ve sıklıkla kullanırız.

Bu makaleyi okumaya devam etmeden önce, .Net Framework 4.0 ile birlikte gelecek olan Task Parallel Library hakkında yazdığım şu yazıyı (TPL (Task Parallel Library) – Task Class) okumanızı tavsiye ederim.

Task Parallel Library ile gelen Parallel sınıfının For methodunu kullanarak, Multi-Core destekli for döngüleri oluşturabiliriz.

Multi-Core destekli olması, Multi-CPU (birden çok CPU içeren) bilgisayarlarda, döngünün CPU’lara dağıtılabilmesini sağlıyor.

Parallel Library‘den önce for döngüsü;

//10 adımlı döngü (0 dahil, 10 hariç) for (int iLoop = 0; iLoop < 100; iLoop++) { // Yapılacak İş }</pre> Parallel Library ile for döngüsü;

//10 adımlı döngü (0 dahil, 10 hariç)
Parallel.For(0, 10, iLoop => {
    // Yapılacak İş
});

Parallel.For metodunu kullarak örnek bir proje hazırladım, projenin kaynak kodlarına buradan ulaşabilirsiniz.

NormalFor_ParallelFor_1

Uzun tek bir döngü yazmak yerine, içiçe iki döngü yazdım, böylece, CPU ve İşletim Sistemi’nin çok adımlı döngülerde yaptığı iyileştirmeleri bir miktar azaltmayı ve daha doğru bir sonuç elde etmeyi amaçladım.

Döngülerden bir tanesini gene de uzun tuttum (100.000.000 - Yüz Milyon Adım), böylece, eğer Framework’ün kendisi döngülerde iyileştirme yapıyorsa, bunları yakalamayı hedefledim.

Diğer döngüyü ise nispeten daha kısa (500 - Beş Yüz) tuttum.

NormalFor_ParallelFor_2

Böylece toplamda 50.000.000 (Elli Milyar) adımlı döngü oluşmuş oluyor.

Gelelim, kodlara;

private void btnNormalDongu_Click(object sender, EventArgs e)
{
    int Sayac = 0;
    Stopwatch sw = Stopwatch.StartNew();
    for (int iLoop = 0; iLoop < (int)txtTekrarAdedi.Value; iLoop++)
    {
        for (int yLoop = 0; yLoop < (int)txtDonguAdedi.Value; yLoop++)
        {
            Sayac++;
        }
    }
    sw.Stop();

    lblNormalDonguSonuc.Text = string.Format("{0:0,0} ms.", sw.ElapsedMilliseconds);
}

ve

private void btnParalelDongu_Click(object sender, EventArgs e)
{
    int Sayac = 0;
    Stopwatch sw = Stopwatch.StartNew();
    Parallel.For(0, (int)txtTekrarAdedi.Value, i => {
        Parallel.For(0, (int)txtDonguAdedi.Value, y => {
            Sayac++;
        });
    });
    sw.Stop();

    lblParalelDonguSonuc.Text = string.Format("{0:0,0} ms.", sw.ElapsedMilliseconds);
}

Teker teker butonlara bastığımızda, for döngüleri çalışacak ve sonuçları Label'larda göreceğiz.

Uygulamayı kendi bilgisayarımda çalıştırdığımda aldığım sonuçlar;

![NormalFor_ParallelFor_3](/assets/uploads/2009/12/NormalFor_ParallelFor_3.png "NormalFor_ParallelFor_3")

*Normal for döngüsü:* **888.700 ms.** (yaklaşık 14 dakika 48 saniye)

*Paralel for döngüsü:* **219.326 ms.** (yaklaşık 3 dakika 39 saniye)

*Aradaki fark:* **75%**

Uygulamayı çalıştırdığım bilgisayarın özellikleri;

![NormalFor_ParallelFor_4](/assets/uploads/2009/12/NormalFor_ParallelFor_4.png "NormalFor_ParallelFor_4")

Engin Polat hakkında

Chief Architect, Microsoft RD, Microsoft MVP

Ada ve Ege'nin babası ;)

Kategoriler

İstatistik

Makale Adedi: 459

Creative Commons Lisansı