Challenge 6

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Challenge 6

Mesaj gönderen thelvaci »

Bir sonraki challenge'da deadlock'tan bahsedeceğimizi ifade etmiştim. Sadettin'in Challange 5'de bu konuya atıf yapmasını da fırsat bilerek yeni bir challange ile karşınızdayım.

Yazdığınız programlarınızda thread kullanıyorsanız eğer başınıza bazı sıkıntılı işler gelmiş olabilir. Örneğin; uygulamanızda birden fazla thread aktif olarak çalışıyor ve siz uygulamanızın ana formunun OnClose olay yöneticinde kapanış esnasında ilgili thread'lerin bitmesini beklemek istiyorsunuz ve bu bekleme işlemi bir türlü sonlanmıyor; siz de IDE'den çalıştırıyor iseniz programınızı Ctrl+F2 ile kırıyor musunuz ya da Görev Yöneticisi üzerinden programı sonlandırıyor musunuz ?

Yada programınız daha kullanıcı dostu olsun diye sağa sola serpiştirdiğiniz thread'leriniz uygulamanızın ana thread'ini bloke mi ediyor ? Ana formunuz hiç yanıt vermez duruma mı geliyor ?

Eğer yukarıdaki sorulardan bir yada birden fazlasına evet dedi iseniz; kuvvetle muhtemeldir ki uygulamanızda deadlock'lar oluşuyor. Sizlere bunların nasıl oluştuğunu ve nasıl bertaraf edileceğini izah etmeye gayret edeceğim ama her zaman olduğu gibi önce sizlerin görüşlerini ve katılımını rica ediyorum.

Lütfen bu paylaşımları birer test olarak görmeyin ve doğru yada yanlış fikirleriniz ile katılımda bulunun. Sitenin müdavimlerinin dahi; challenge'lara katılmıyor olmasının bir nedeni var ise bunu da bilmek isterim gerçekten. Bilmemek ayıp değildir, hepimiz herşeyi bilemeyiz zaten; ama araştırmamak, kendini geliştirmemek, kendinizi geliştirebilmenize imkan sağlamaya çalışanlara kayıtsaz kalmak; işte o bence ayıptır.

Herneyse; sorularımıza geçelim müsaadeye binaen;

1.Soru: Uygulamanız içinde bir thread'iniz olacak ve bu thread ile ana thread birbirlerini bloke edecekler. Nasıl ?
2.Soru: Uygulamanızın ana thread'i bloklanmadan 2 ayrı thread'in birbirini bloklamasını sağlayabilir misiniz. Nasıl ?
3.Soru: Uygulamanızın kapanışı esnasında yada herhangi bir an diliminde; uygulamanız içinde yaşayan thread'lerin; durumlarını sorgulayabilir ve bloke olup olmadıklarını kontrol edebilir misiniz. Nasıl ?

Not: İnananlar için paylaşmak; Allah'ın size nasip ettiği ilmin zekatıdır.
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 6

Mesaj gönderen thelvaci »

Hiçkimse mi threading kullanmıyor ya da herkes mi doğru kullanıyor threadleri ?
Kullanıcı avatarı
sadettinpolat
Moderator
Mesajlar: 2131
Kayıt: 07 Ara 2003 02:51
Konum: Ankara
İletişim:

Re: Challenge 6

Mesaj gönderen sadettinpolat »

Resim
Deadlock (Temsili)
thelvaci yazdı: 1.Soru: Uygulamanız içinde bir thread'iniz olacak ve bu thread ile ana thread birbirlerini bloke edecekler. Nasıl ?


Soruyu cok iyi anlamadim. normalda olmasini istemeyecegimiz bir durumu kendimiz mi olusturmamiz lazim ?
Boyle bir sey istiyorsam ana thread icinde ikinci threadin yapmasi gereken bir isi beklerken ikinci threadin da bu isi yapmasi icin ana thread icinde ki bir islemin meydana gelmesini beklerdim.
bu durumda iki threadde birbirini bekleyeceklerinden aklima sunay akinin su misralari gelirdi.

iki rayi gibiyiz bir tren yolunun
yakin olmasi neyi degistirir son istasyonun :)

thelvaci yazdı: 2.Soru: Uygulamanızın ana thread'i bloklanmadan 2 ayrı thread'in birbirini bloklamasını sağlayabilir misiniz. Nasıl ?


a seceneginde ki cevapla ayni sekilde.

thelvaci yazdı: 3.Soru: Uygulamanızın kapanışı esnasında yada herhangi bir an diliminde; uygulamanız içinde yaşayan thread'lerin; durumlarını sorgulayabilir ve bloke olup olmadıklarını kontrol edebilir misiniz. Nasıl ?
ben genelde threadlere ThreadBittiMi diye bir degisken koyup form close olayinda bu degiskenin degerini kontrol ediyorum.
belirli bir sure sonra thread sonlanmadiysa gozumu karartip threadi sonlandirip akabinde de programi sonlandiriyorum.
"Sevmek, ne zaman vazgececegini bilmektir." dedi, bana.

---
http://sadettinpolat.blogspot.com/
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 6

Mesaj gönderen thelvaci »

Teşekkür ederim yanıtına Sadettinim. Evet senin de ifade ettiğin gibi normalde olmasını istemediğimiz bir senaryoyu istiyoruz. Doğru söylüyorsun iki thread'in birbirini beklemesi ve deadlock'a ulaşmak hedefimiz. Böylece yapmamamız gerekenleri göstermiş olacak bu senaryo. Evet; ama nasıl kısmını da yanıtlamanızı istiyorum. Hangi yöntemler ile birbirini bloklayan ve deadlock oluşturan sonuçları görebiliriz. Bunu paylaşmanızı istiyorum esasen.

Üçüncü sorumda ise bir değişkenin durumunu kontrol ederek işlem yapamazsın. Kontrol edeceğin thread'ler bloke durumda çünkü ;) Ayrıca thread'ler uzun zaman bloke olmuş ise gözümü karartırım thread'in kafasına kabak atar öldürürüm demek de olmaz :) İşimiz onların bloklanmamasını sağlamak.

İşte bu neden ile bu challange'ı açtım; çok iyi biliyorum çünkü pek çok arkadaşımız senin söylediğin gibi yapıyor; ama yapılması gereken bu değil. Bu nedenle sorumu yineliyorum; thread'leri sorduğum kriterler ile bloke edebilir misiniz ?

Nasıl bloke olduklarını ve nedenlerini gözlemleme de bir faydamız olabilir ise eğer; bu durumda bloke olmayan yada daha zor bloke olan thread'ler yazabiliriz ve programımız daha güvenli olur.

Üçüncü sorumun yanıtını veren bir API grubu var diyerek de bir ipucu vermiş olayım ;)
ikra
Üye
Mesajlar: 900
Kayıt: 28 Nis 2005 01:26
Konum: Simdilik Topragin Üstü

Re: Challenge 6

Mesaj gönderen ikra »

Criticalsection'a enter yapip leave yapmadan ikinci thread'i kilitleyebilecegimi saniyorum. Ana thread'da waitfor kullanip ara thread'da ise criticalsection ile bloke ettik mi, ana thread sen gelmez oldun sarkisinin sözlerini yazmaya baslar :)
Yada pool kullanip tüm threadleri bir execute prosedürüne yönlendirip execute prosedürünün girisini kilitledik mi tüm pool icindeki worker'lar kilitlenmis olurlar.
kıdemsiz üye
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 6

Mesaj gönderen thelvaci »

ikra yazdı:Criticalsection'a enter yapip leave yapmadan ikinci thread'i kilitleyebilecegimi saniyorum. Ana thread'da waitfor kullanip ara thread'da ise criticalsection ile bloke ettik mi, ana thread sen gelmez oldun sarkisinin sözlerini yazmaya baslar :)
Yada pool kullanip tüm threadleri bir execute prosedürüne yönlendirip execute prosedürünün girisini kilitledik mi tüm pool icindeki worker'lar kilitlenmis olurlar.
Basit de olsa örnekleri görelim :) Daha sonra bu başlık altındaki örnekleri arzu ederseniz tartışırız da. İki kilitleme yöntemini ben ipucu olarak vereyim madem;

Bunlar bizim en sık yaptığımız kilitlenmelerin başrol oyuncuları;

1- TThread.Synchronize
2- SendMessage

Bunlar ile de kilitlenme işlemini gözlemleyebilir misiniz ?
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 6

Mesaj gönderen thelvaci »

Unutmadan bir tane daha vereyim; GetWindowText ;)

Bu kilitlenmeleri bilfiil test edip gözlemleyebilir misiniz ?
Kullanıcı avatarı
sadettinpolat
Moderator
Mesajlar: 2131
Kayıt: 07 Ara 2003 02:51
Konum: Ankara
İletişim:

Re: Challenge 6

Mesaj gönderen sadettinpolat »

Abi soyle bir thread olsa

Kod: Tümünü seç

  TThreadDeneme = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;

  public
    Sonuc:Boolean;
  end;



procedure TThreadDeneme.Execute;
var
deger:string;
begin
sonuc := False;

Synchronize(
  procedure
  begin
    deger := Form26.edit1.text;
  end
  );

if deger = 'deadlock' then
begin
  while deger = 'deadlock' do
  begin
      Synchronize(
        procedure
        begin
          deger := Form26.edit1.text;
        end
        );
     Sleep(1);
  end;
end;
sonuc := True;
end;



ana threadde soyle olsa

Kod: Tümünü seç

procedure TForm26.Button1Click(Sender: TObject);
var
myThread:TThreadDeneme;
begin

myThread := TThreadDeneme.Create(True);
myThread.Start;

while myThread.sonuc = false do
   Application.ProcessMessages;


 ShowMessage('bitti');


end;


edit1.text = deadlock olursa iki threadi de kilitlemis oluruz. sonsuza kadar bekler dururlar
"Sevmek, ne zaman vazgececegini bilmektir." dedi, bana.

---
http://sadettinpolat.blogspot.com/
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 6

Mesaj gönderen thelvaci »

Ama ana thread hâla responsive durumda ;) Öyle bir şey yapın ki ana thread'de gümlesin. Hiçbirşeye cevap veremez hale gelsin.
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 6

Mesaj gönderen thelvaci »

Örneğin:

Kod: Tümünü seç

unit uThreadBlockingChallange;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    DummyThread,
    DummyThread2 : TThread;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure L(const AMessage : String);
begin
  form1.Memo1.Lines.Add( AMessage );
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DummyThread :=
    TThread.CreateAnonymousThread(
                                  procedure
                                  begin
                                    TThread.CurrentThread.FreeOnTerminate := false;

                                    Sleep(1000);
				    // Ana thread context'inde bir metod çağrımı yapalım.	
                                    TThread.Synchronize(
                                                        nil,

                                                        procedure
                                                        begin
                                                          L('Ana threade benden selam olsun...');
                                                        end
                                                       );
                                  end
                                 );

  DummyThread.Start;

  // Thread'in bitmesini bekleyelim
  WaitForSingleObject(DummyThread.Handle, INFINITE);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DummyThread2 :=
    TThread.CreateAnonymousThread(
                                  procedure
                                  begin
                                    TThread.CurrentThread.FreeOnTerminate := false;

                                    Sleep(1000);
                                    // Ana thread'e mesaj gönderelim.
                                    SendMessage(Application.MainForm.Handle, WM_NULL, 0, 0);
                                  end
                                 );

  DummyThread2.Start;

  // Thread'in bitmesini bekleyelim
  WaitForSingleObject(DummyThread2.Handle, INFINITE);
end;

end.

Kullanıcı avatarı
sadettinpolat
Moderator
Mesajlar: 2131
Kayıt: 07 Ara 2003 02:51
Konum: Ankara
İletişim:

Re: Challenge 6

Mesaj gönderen sadettinpolat »

thelvaci yazdı:Ama ana thread hâla responsive durumda ;) Öyle bir şey yapın ki ana thread'de gümlesin. Hiçbirşeye cevap veremez hale gelsin.

abi basit , dongudeki app.ProcessMessages ' i kaldirdik mi tamamdir :D
"Sevmek, ne zaman vazgececegini bilmektir." dedi, bana.

---
http://sadettinpolat.blogspot.com/
Kullanıcı avatarı
sadettinpolat
Moderator
Mesajlar: 2131
Kayıt: 07 Ara 2003 02:51
Konum: Ankara
İletişim:

Re: Challenge 6

Mesaj gönderen sadettinpolat »

adam bosuna yazmamis Don’t use the TThread.Synchronize procedure diye :)


Don’t use the TThread.Synchronize procedure. Recent versions of Delphi include TThread.Queue, which is asynchronous, and so avoids this deadlock.

https://marc.durdin.net/2012/08/waitfor ... er-use-it/
"Sevmek, ne zaman vazgececegini bilmektir." dedi, bana.

---
http://sadettinpolat.blogspot.com/
thelvaci
Kıdemli Üye
Mesajlar: 770
Kayıt: 11 Tem 2010 07:17
Konum: Istanbul
İletişim:

Re: Challenge 6

Mesaj gönderen thelvaci »

Evet, en sık karşılaşılan hatalardan birisidir. Gerçekten emin isek TThread.Synchronize çağrımını yaptığımız thread'in ana thread tarafından beklenmediğine o durumda kullanabiliriz, bir sakıncası yok. Ancak; ana thread'in herhangi bir thread'i yada o thread içinde kapalı konuma geçmiş(nonsignaled) bir kernel nesnesini beklediği bir durum var ise ve o thread'de ana thread'e TThread.Synchronize ile ulaşıyorsa; o zaman Deadlock ile karşılaşırız. Kısacası, ortak kaynakların birbirlerini bekledikleri kimsenin bir adım ilerleyemediği senaryodur ve maalesef sıklıkla karşılaşılır. O sebep ile bu konuyu açma ihtiyacı hissettim. TThread.Synchronize en bariz örneklerden birisidir. Ancak; SendMessage'da ikinci en sık deadlock senaryolarından birisidir. SendMessage çağrımı mesajın alındığı pencerenin kuyruk yöneticisi tarafından direkt işletilir ve geriye döner. Yani SendMessage çağrımını yapan thread çağrım bitene kadar bloklanacaktır. Mesajın ulaşmaya çalıştığı thread'de bloking durumda ise ve işin kötüsü mesajı göndereni bekliyor ise bu durumda karşımıza yine Deadlock çıkar.

O yüzden multithreading zor olarak algılanır ve tasarıma dikkat edilmez ise gerçekten de zor hâle kolaylıkla gelebilir. İşin özünde yatan; kaynakların efektif kullanımını sağlamak ve aynı anda aynı kaynağa erişimleri organize etmektir. Esas amaç, bir kaynaktan yararlanmak için bekleyen unsurların adil organizasyonunu sağlayabilmektir. Neden adil ifadesine vurguda bulundum ? Bunun sebebi Challange 5'de Sadettin'in bahsini açtığı starvation (açlık) kavramına vurgu yapmak için.

Bir kaynağı paylaşması gereken unsurların paylaşılan kaynağı adil kullanmasını sağlamazsanız; bekleyen unsurlardan bir yada bir kaçı kaynağı uzun süre kullanamayacaktır. Bu duruma starvation açlık adı verilir. Örneğin; elektrik faturanızı ödemek için vezne kuyruğunda bekliyorsunuz ve veznedarın akrabaları, arkadaşları, diğer veznedarların arkadaşları vs. sürekli gelip sizin önünüze geçiyor ve durum sürekli bu şekilde işliyor. Bu durumda siz asla faturanızı yatıramıyorsunuz. İşte buna kaynağı bekleyen unsurun açlığı adı verilir. İşletim sistemi de thread'ler arasındaki açlığı engellemek için çeşitli mekanizmalara sahiptir. Örneğin işletim sistemi üzerinde çalışan 5 tane realtime thread, 4 tane high priority thread ve 1 tanede gariban low priority thread olsun. İşletim sistemi önce realtime önceliğe sahip olan thread'leri işleyip bitirmeye çalışacaktır; ardından high ve en sonda gariban thread'e sıra gelecektir. Ancak tam gariban thread'e sıra gelecekken yine bir realtime thread gelirse ne olacaktır. Bu durumda bizim gariban thread açlıktan geberecekmidir :) Hayır elbette öyle olmayacaktır, bu gibi bir durumun oluşmaya başladığını anlayan işletim sistemi ilgili thread'in önceliğini bir müddetliğine yükseltecek ve onun da gönlünü alacak sonra önceliğini tekrar eski durumuna geri çekecektir. Bu işleme priority boosting demişler.

Kısacası threading esasen kolay bir konudur, yeterki kısıtlı kaynaklarınızın olduğunu bilin ve kaynakları adil bir şekilde yönetmenize yardımcı olması için hizmetinize sunulan senkronizasyon nesnenerini düzgün bir şekilde kullanın. O zaman çok ciddi sorunlar ile karşılaşma ihtimaliniz epey bir azalır.

Neyse konumuz açlık değil elbette, ama aklıma gelince bir iki kelam edeyim dedim. Yukarıda gönderdiğim örneklerdeki deadlockları çözmek için ilkinde; TThread.Synchronize yerine TThread.Queue yazabilirsiniz. Bu metod; uygulamanızın ana thread'inin bloklu olup olmadığı ile ilgilenmez; yapacağı işler kuyruğunun en altına bir işi atıp geri döner. Ana thread'iniz yaptığı işi bitirdiğinde bir sonraki işi kuyruktan alıp işleyebilir ve deadlock'a müsaade edilmemiş olur.

İkinci örnekte; SendMessage yerine PostMessage kullanabilirsiniz. Dediğim gibi SendMessage blocking bir API'dir ve karşıda mesajı alacak olan thread'in mesajı işleyip geriye dönmesini bekler. Oysaki PostMessage tıpkı TThread.Queue gibi davranır ve ilgili mesajın hedef thread'in mesaj kuyruğunun en sonuna ekler. İlla SendMessage çağrımı yapmam gerekiyor diyorsanız; SendMessage yerine SendMessageTimeOut'u kullanmanızı öneririm.

Ayrıca en son olarak belirttiğim GetWindowText API'si gibi başka API'lerde vardır ve kullandığınız API'lerin msdn açıklamalarını okumak için zaman ayırmanızın önemli olduğunu söyleyebilirim. GetWindowText API'si de yukarıdaki örnekte deadlock'a yol açacaktır çünkü iç yapısında hedef pencerenin başlık bilgisini elde edebilmek için hedef pencereye SendMessage API'si ile WM_GETTEXT mesajını iletir ve yanıtını bekler.

Bu ve buna benzer Deadlock senaryolarını çoğaltabilirsiniz. Bu nedenle, birden fazla thread ile işlem yapacak ve ortak kaynaklara müracaat edecekseniz; gidin bir bardak kahve alın ve önce bir kağıt üzerinde hangi thread hangi kaynağa ne zaman ve nasıl ulaşacak diye yazın/çizin. Ardından kodlamaya geçin.

Şimdi; bu bilgilerin ışığında artık bir yada birden fazla thread'in birbirlerini beklemelerinden kaynaklanan Deadlock senaryolarını oluşturabileceğinize göre; 3ncü soru hakkında ne düşünüyorsunuz ?

Deadlock durumda olan, yada sonsuz bekleme durumunda olan thread'lerimizi nasıl tespit edeceğiz ?
Kullanıcı avatarı
sadettinpolat
Moderator
Mesajlar: 2131
Kayıt: 07 Ara 2003 02:51
Konum: Ankara
İletişim:

Re: Challenge 6

Mesaj gönderen sadettinpolat »

benim anlamadigim kisim surasi.

WaitForSingleObject , thread bitene kadar ana threadi bekletiyor. bu bekleme esnasinda ana threade herhangi bir mesaj bile iletilmiyor.
madem ana thread ikinci threadi bekleyecek neden thread kullaniyoruz ? ikinci threadde ki islemi ana thread icinde yapsak yine ayni kapiya cikmiyor muyuz ?
"Sevmek, ne zaman vazgececegini bilmektir." dedi, bana.

---
http://sadettinpolat.blogspot.com/
Kullanıcı avatarı
sadettinpolat
Moderator
Mesajlar: 2131
Kayıt: 07 Ara 2003 02:51
Konum: Ankara
İletişim:

Re: Challenge 6

Mesaj gönderen sadettinpolat »

debugger ekranindan bakip anlarim :)

Resim


Wait Chain Traversal ile bulunuyormus ama detayini tam olarak bilmiyorum
"Sevmek, ne zaman vazgececegini bilmektir." dedi, bana.

---
http://sadettinpolat.blogspot.com/
Cevapla