Thread | Engin Polat\'ın Windows 8 , Windows Phone 8 ve C# içerikli programcılık sitesi

Arşiv

Etiketlenen yazılar thread

C# ile uzun süren method’ları kontrol altına alın

18 October 2013 Yorum yapılmamış

Uygulama geliştirirken karşılaştığımız birçok senaryoda çağırdığımız bir method’un uzun sürede cevap üretmemesinden dolayı kaynaklarımızı tükettiğini görebiliriz.

Çağırılan method’un belli bir zaman aşımı süresine sahip olması ve bu süre sonuna kadar değer üretmediyse sonlanması için birçok yöntem kullanabiliriz, fakat ben System.Threading namespace‘inde yeralan CancellationTokenSource sınıfını kullanan aşağıdaki yöntemi tercih ediyorum;

/// <summary>
/// Timeout süresinde tamamlanmayan method'u otomatik sonlandırır
/// </summary>
/// <param name="action">Çağırılacak method</param>
/// <param name="timeout">Milisaniye cinsinden zamanaşımı süresi</param>
/// <returns></returns>
public static bool Execute(Action action, int timeout)
{
	var tokenSource = new CancellationTokenSource();

	var token = tokenSource.Token;

	var task = Task.Factory.StartNew(action, token);

	if (!task.Wait(timeout, token))
	{
		tokenSource.Cancel();

		return false;
	}

	task.Dispose();

	return true;
}

/// <summary>
/// Timeout süresinde tamamlanmayan method'u otomatik sonlandırır
/// </summary>
/// <param name="action">Çağırılacak method</param>
/// <param name="timeout">Milisaniye cinsinden zamanaşımı süresi</param>
/// <returns></returns>
public static Tuple<bool, T> Execute<T>(Func<T> action, int timeout)
{
	var result = Tuple.Create(false, default(T));

	var tokenSource = new CancellationTokenSource();

	var token = tokenSource.Token;

	var task = Task.Factory.StartNew(() => { result = Tuple.Create(true, action.Invoke()); }, token);

	if (!task.Wait(timeout, token))
	{
		tokenSource.Cancel();
	}

	task.Dispose();

	return result;
}

Böylece değer döndürmeyen method’lar eğer timeout‘a uğramışsa geriye bool tipinde false değeri, timeout‘a uğramamışsa true değeri döndürebiliyoruz.

Geriye değer döndürecek method’lar eğer timeout‘a uğramışsa geriye Tuple sınıfından yeni bir değer döndürüyoruz. İki property’li Tuple‘ın birinci property’si timeout olup/olmadığını belirten bool tipinde, ikinci property ise çağırılan method’dan dönecek cevabın tipinin varsayılan değeri.

Bu methodlar sayesinde hatalar veya uzun süren method’ların kaynaklarımızı tüketmesi sorunları ile uğraşmadan uygulamalarımızı geliştirebiliriz.

Örnek kullanımlar;

Geriye değer döndürmeyen method’ları aşağıdaki örnek kullanım ile çağırabiliriz

var result = Execute(LongRunningProcess, 3000);

Geriye değer döndüren method’ları (örneğin int) aşağıdaki örnek kullanım ile çağırabiliriz

var result = Execute<int>(LongRunningProcess, 5000);

Windows Phone uygulamalarında EnableFrameRateCounter özelliği

26 April 2013 Yorum yapılmamış

Windows Phone uygulamaları geliştirirken elimizin altında basit ama önemli performans ipuçlarının olması işimize yarar.

EnableFrameRateCounter özelliğini açmak

  • Render Thread FPS
  • User Interface Thread FPS
  • Texture Memory Usage
  • Surface Counter
  • Intermediate Texture Counter
  • Screen Fill Rate

değerlerinin ekranda gözükmesini sağlar;

Bu değerler şu anlamlara gelir;

Render Thread FPS
Ekran’ın güncellenme sıklığı. 60 fps civarı güncelleme hızı son kullanıcıya iyi bir deneyim sunacaktır, 30 fps ve üzeri ise kabul edilebilir bir deneyim sunacaktır. Ekran güncelleme hızı 30 fps altınaa düştüğünde bu değer kırmızı olacaktır, dikkat etmek lazım!

User Interface Thread FPS
O esnada çalışan UI thread’in güncellenme sıklığı. Data Binding, animasyonlar ve property change notifications bu thread’de işletilirler. Eğer 15 fps altına düşerse bu değer kırmızı olacaktır, dikkat etmek lazım!

Texture Memory Usage
Çalışan uygulamada kullanılan Texture’ların kullandığı grafik hafıza miktarı

Surface Counter
Graphical Processing Unit (GPU) tarafından işlenecek yüzey miktarı

Intermediate Surface Counter
Önbelleklenmiş bellekler dahil işlenecek yüzey miktarı

Screen Fill Rate
Ekranda güncellenecek piksel miktarı. 1 değeri ekran çözünürlüğü kadar anlamına geliyor, örneğin 480×800. Eğer 1’in altına düşerse veya 2’nin üzerine çıkarsa kırmızı olur, dikkat etmek lazım!

App.xaml.cs dosyasında yeralan

Application.Current.Host.Settings.EnableFrameRateCounter = true;

satırı ile performans ipuçlarını ekranda göstermeye başlayabiliriz.

Uygulamanın son halini MarketPlace‘e göndermeden önce performans ipuçlarının ekranda gözükmediğinden emin olmamız gerekiyor.

Yukarıda ekranın bir parçasının gözüktüğü örnek uygulama için MainPage.xaml dosyasını aşağıdaki şekilde güncellemek gerekiyor;

<phone:PhoneApplicationPage
	x:Class="EnableFrameRateCounterTest.MainPage"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
	xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	mc:Ignorable="d"
	FontFamily="{StaticResource PhoneFontFamilyNormal}"
	FontSize="{StaticResource PhoneFontSizeNormal}"
	Foreground="{StaticResource PhoneForegroundBrush}"
	SupportedOrientations="Portrait" Orientation="Portrait"
	shell:SystemTray.IsVisible="True">

	<phone:LongListSelector Name="lstMessages" />

</phone:PhoneApplicationPage>

MainPage.xaml.cs dosyasında aşağıdaki değişiklikler yapılmalı;

public MainPage()
{
	InitializeComponent();

	var messageList = new List<string>();

	for (int iLoop = 0; iLoop < 150; iLoop++)
	{
		messageList.Add("Long message line #" + iLoop);
	}

	lstMessages.ItemsSource = messageList;
}

Thread.Sleep ve Thread.SpinWait arasındaki fark

26 February 2011 2 yorum

Thread sınıfı ile ilgili en çok karşılaştığım sorulardan birisi; “Thread.Sleep() ile Thread.SpinWait() method’ları arasındaki fark nedir?” olmuştur.

MSDN Process ve Thread dökümanları ne yazık ki bu konuda bize yeterli bilgi sağlamıyor.

Peki Thread sınıfının SpinWait() ve Sleep() method’ları arasındaki fark nedir?

Programming .Net Components kitabının yazarı Juval Löwy‘den alıntı (sayfa 192) yapacağım;

Orjinal : When a thread calls SpinWait(), the calling thread waits the number of iterations specified, and the thread is never added to the queue of waiting threads. As a result, the thread is effectively put to sleep without relinquishing the remainder of its CPU time slot.

The .NET documentation does not define what an iteration is, but it is likely mapped to a predetermined number (probably just one) of NOP (no-operations) assembly instructions. Consequently, the following SpinWait() instruction will take different time to complete on machines with different CPU clock speeds:

const long MILLION = 1000000;
Thread.SpinWait(MILLION);

SpinWait() is not intended to replace Sleep(), but is rather made available as an advanced optimization technique. If you know that some resource your thread is waiting for will become available in the immediate future, it is potentially more efficient to spin and wait, instead of using either Sleep() or a synchronization object, because those force a thread context switch, which is one of the most expensive operations performed by the operating system. Even in the esoteric cases for which SpinWait() was designed, using it is an educated guess at best. SpinWait() will gain you nothing if the resource is not available at the end of the call, or if the operating system preempts your thread because its time slot has elapsed, or because another thread with a higher priority is ready to run. In general, I recommend that you should always use deterministic programming (using synchronization objects in this case) and avoid optimization techniques.

Alıntı’nın önemli cümleleri;

Türkçesi : Eğer bir thread SpinWait() method’unu çağırırsa, parametre ile belirtilen CPU çevrimi boyunca beklemeye geçer ve Bekleyen Threadler (Waiting Threads) listesine kendini eklemez. Böylece, CPU’da bulunduğu slot’tan vazgeçmeden, uyku moduna geçmiş olur.

.NET dokümantasyonuna göre her CPU çevriminde (muhtemelen) bir adet NOP (no-operations) assembly komutu çalıştırır. Fakat, aşağıdaki SpinWait() komutu, farklı hızlarda çalışan CPU’larda farklı sürelerde tamamlanır.

const long MILLION = 1000000;
Thread.SpinWait(MILLION);

SpinWait() method’u, Sleep() method’unun yerine düşünülmemiştir, daha ileri seviye optimizasyon yapılabilmesi için amaçlanmıştır. Eğer çalışan thread’in ihtiyaç duyduğu kaynak, çok kısa süre içerisinde uygun olacaksa (mesela başka bir thread kaynak üzerinde bulunan lock’ı kaldıracaksa) Sleep() yerine SpinWait() kullanmak daha uygun olur.

Sleep() method’u (veya syncronization nesnesi), thread’in CPU üzerinde context switch yapmasına sebep olur.

Son cümle; SpinWait() method’u, çağıran thread’i, işlemci üzerinde aktif tutar, Sleep() method’u ise, thread’i belirli süre boyunca gerçek anlamda uyutur.

TPL (Task Parallel Library) – Task Class

06 December 2009 Yorum yapılmamış

.Net 4.0 ile birlikte gelecek güzel class’lardan bir tanesi de Task class’ı.

Task class’ı ve TPL (Task Parallel Library) ile birlikte gelen diğer class’lar sayesinde paralel programlama .net framework ile desteklenir hale geliyor.

TPL_Task_1

Yandaki görselde görebileceğiniz gibi, TPL (Task Parallel Library) ve PLINQ (Parallel LINQ) .Net Framework 4.0 ile gelen en önemli özellikler.

Paralel programlamanın performans’a katkısını MSDN’de yer alan şu iki makale’de okuyabilirsiniz;

Parallel Programming in .Net Framework

Parallel Performance

Gene MSDN’de yer alan şu sayfalardan da .net 4 ile gelecek yeni yapıları öğrenebilirsiniz;

Data Structures for Parallel Programming

Task Parallelism (Task Parallel Library)

Introduction to Tasks

Task class’ını kullanarak, Thread yönetme maliyetlerinden kurtularak kolaylıkla Multi-Core uyumlu, Asenkron çalışabilen kodlar yazabiliriz.

Task class’ından yeni bir örnek oluşturmak için;

Aşağıdaki gibi yeni bir instance oluşturup, daha sonra Start method’u ile başlatabiliriz.

Task t0 = new Task(() =>
	MessageBox.Show("Task Örneği 1")
);
t0.Start();

Veya, aşağıdaki kodda yazdığım gibi, Task class’ının singleton static Factory property’sinin StartNew method’u ile hem tanımlayıp, aynı anda başlatabiliriz.

Task t1 = Task.Factory.StartNew(() =>
{
	string Mesaj = "Birinci çalışacak..";
	MessageBox.Show(Mesaj);
});

Örneğini oluşturduğumuz Task nesnesi, başlatılana kadar hafızada bekler. Başlatıldığında, ayrı bir thread’de çalışacağından dolayı, uzun süren işlerin arayüz’ü kilitlemesini önlemek için kullanılabilir.

Birden Fazla Task’ın peşpeşe çalışması sağlanabilir;

Task t2 = Task.Factory.StartNew(() =>
{
	MessageBox.Show("Birinci çalışacak..");
}).ContinueWith((t) =>
{
	MessageBox.Show("İkinci çalışacak..");
}).ContinueWith((t) =>
{
	MessageBox.Show("Üçüncü çalışacak..");
});

Veya birden fazla Task’ın aynı anda çalışması da sağlanabilir;

Task t3 = Task.Factory.StartNew(() =>
{
	Task c1 = Task.Factory.StartNew(() =>
	{
		MessageBox.Show("Aynı anda çalışacak..");
	}, TaskCreationOptions.AttachedToParent);

	Task c2 = Task.Factory.StartNew(() =>
	{
		MessageBox.Show("Aynı anda çalışacak..");
	}, TaskCreationOptions.AttachedToParent);
});

TaskCreationOptions parametresine verdiğimiz AttachedToParent değerine dikkat!.. Bu sayede c1 ve c2 Task’ları t3 Task’ının Child Task‘ları haline geldi, t3 Task’ı da Parent Task oldu.

DataTable ve DataReader nesnelerini yarıştıralım

23 November 2009 Yorum yapılmamış

.Net ortamında veriye erişim modeli olarak kullanabileceğimiz iki seçenek vardır; Bağlantılı (Connected) ve Bağlantısız (Disconnected) Ortam.

  • Bağlantılı veri erişim modelinde, veriye erişmek için DataReader classını,
  • Bağlantısız veri erişim modelinde ise, genellikle DataTable classını kullanırız.

Bu yazımda, bu iki class’ı benzer şartlar altında yarıştıracağız ve hangisinin veriyi daha hızlı getirdiğine karar vereceğiz.

DataTable_vs_DataReader

Projenin kaynak kodlarını buradan download edebilirsiniz.

Kaynak kodlara baktığınızda görebileceğiniz gibi, proje tek form’dan oluşuyor. Formun Load event’inde, AdventureWorks2008R2 veritabanına bağlantı açılıyor ve PR_TABLO_LISTESI procedure’ü çalıştırılıyor.

PR_TABLO_LISTESI procedure’ünün kodları;

CREATE PROCEDURE PR_TABLO_LISTESI
AS
DECLARE @T TABLE (TABLO_ADI VARCHAR(100), SATIR_SAYISI INT)

INSERT INTO @T
EXEC sp_msForEachTable 'SELECT ''?'', COUNT(*) FROM ? WITH (NOLOCK)'

SELECT * FROM @T ORDER BY SATIR_SAYISI DESC

Gördüğünüz gibi, procedure’ün yaptığı çok fazla birşey yok. Pek fazla bilinmeyen ve dökümante edilmemiş sp_msForEachTable sistem prosedür’ünü kullanarak veritabanında bulunan her tablonun ismini ve satır sayısını döndürüyor.

Bu sistem prosedür’ünün nasıl kullanıldığını başka bir yazıda anlatmayı planlıyorum. (Eminim BilgeAdam’daki öğrencilerim bu prosedür’ü hatırlayacaklardır)

Form’un Load eventinin devamında, prosedür’den dönen liste (tablo isimleri ve satır sayıları) lvTablolar ismindeki ListView component’ine dolduruluyor.

cmbTekrarAdet isimli combobox’da yer alan (5 Adet, 10 Adet, 20 Adet) elemanlarından varsayılan olarak 10 Adet elemanı seçili olarak geliyor. Bu combobox, testin peşpeşe kaç defa tekrar edeceğini belirliyor. Böylece bilgisayardaki anlık performans değişikliklerine karşı önlem almış oluyoruz.

Yarış Başlasın Butonunun Click event’inde, yeni bir Thread nesnesini YarisBaslasin() methodunu çalıştıracak şekilde oluşturuyoruz ve başlatıyoruz.

YarisBaslasin() methodunu Arayüz’den (User Interface : UI) ayrı bir thread’den başlatarak, test süresince formda oluşacak kilitlenmeleri önlemiş olduk.

Method’un içerisinde DataTableHesapla() ve DataReaderHesapla() methodları çağırılıyor.

Bu methodlarda TekrarAdet defa (cmbTekrarAdet kontrolünden geliyor) SqlConnection açılıyor, ListView’da seçili tabloya SELECT çekiliyor ve dönen kayıtların üzerinde tek tek geziliyor.

Son olarak, DataTable ve DataReader nesnelerinde bu işlemlerin ne kadar sürdüğü karşılaştırılıyor ve sonuç ekranın altındaki bir label’da gösteriliyor.

Süre ölçmek, ADO.NET Connected ve Disconnected Environment’ı karşılaştırmak için tek başına kullanılacak bir yöntem değil. Fakat en azından bir fikir verebilir.