Thread overview
Eş zamanlı programlama ile ilgili zorlu bir hata
Jul 19, 2012
Salih Dinçer
Jul 19, 2012
Salih Dinçer
Jul 19, 2012
Kadir Can
Jul 19, 2012
Salih Dinçer
Jul 19, 2012
Salih Dinçer
Jul 19, 2012
erdem
July 19, 2012

Gerçekten çok pis bir hataymış, çok şey denedim, çözümü okumadım ve tabii ki beceremedim...:)

Şimdi bu iletiyi onayladıktan sonra iletinin geri kalanını okumalıyım... :rolleyes:

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

July 19, 2012

@Salih;
immutable int[] dediğimiz zaman dizimiz değişmez oluyor. Eğer böyle kullanırsak diziye sadece kurarken atama yapabiliriz, onun dışındaki atamalar hataya sebep olur.
Ama immutable (int)[] dersek sadece dizinin elemanları değişmez olur. Bu durumda diziye eleman ekleyebiliriz; ancak var olan elemanları değiştiremeyiz.
Örneğin;

immutable int[] sayılar;
sayılar ~= 10;  //Derleme hatası: Değişmez diziyi değiştirmeye çalışıyoruz

Üstteki kullanımda dizi değişmezdir, ekleme bile yapamayız.

immutable (int)[] sayılar;
sayılar ~= 10;  // Yasal: Elemanları değişmez olan dizinin sonuna ekleme yapıyoruz

Yukarıdaki kullanımda ise dizinin sadece var olan elemanları değişmez oluyor, ekleme yapabiliyoruz.
Özet olarak, birinci kullanımda immutable bir int[] tanımlarken, ikinci kullanımda immutable int'lerden oluşan bir dizi tanımlıyoruz.

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

July 19, 2012

Keyifle okudum, hele şu satırları...:)

Alıntı (acehreli):

>

Hani atanamazdı! Ah Ali! :) Yukarıdaki bir atama değil, kopyalayarak kurma. b, a'dan kopyalanıyor. Atama olabilmesi için b'nin önceden var olması gerekir...

Evet, atanamıyor:

'deneme.d(86030): Error: variable deneme.main.b cannot modify struct with immutable members'

Yani immutable üyeler değiştirilemezlermiş. Biliyoruuum... :)

Kusura bakmayın uzun oldu ama ben bu konudan çok ders aldım. :)

Özetle çözüme ulaştığına sevindim. Gerçi çözümü çok anlamasam da...:)

Alıntı (acehreli):

>

Bu arada, derleyicinin üye için 'immutable int[]' türünü kabul etmemisinin geçerli nedeni var. Görebiliyor musunuz? Benim onu görmem de biraz zaman aldı.
İşte anlamadığım için de göremiyorum. Yani immutable'ı parantezli kullanınca ne oluyor, kullanmayınca ne oluyor; gerçi ne olduğunu biliyoruz programımız çalışıyor. Ama işte aradaki farkı anlayamadım?

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

July 19, 2012

Ben de sorunun yanıtını kendi başıma bulamadım. Ama adımları denedim. Gerçekten hatayı iyi yakalamışsınız.

Kadir Can'ın mesajını okuduktan sonra ona katılıyorum :) İnce bir ayrıntı..

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

July 19, 2012

Alıntı (Kadir Can):

>
> immutable (int)[] sayılar;
> sayılar ~= 10;  // Yasal: Elemanları değişmez olan dizinin sonuna ekleme yapıyoruz
> ```

> Yukarıdaki kullanımda ise dizinin sadece var olan elemanları değişmez oluyor, ekleme yapabiliyoruz.
Tamam, şimdi anladım. Teşekkürler...

Eklenebilir değişmez dizi şu şekilde oluyor **immutable'(int)'[]**,
Boyutu da değiştirilemeyen ise **immutable(immutable'(int)'[])**'*' ile dört bir yandan kuşatılıyor...:)

Dolayısıyla Ali hocanın en son sorduğu sorunun cevabı çok basit. Biz yapıyı kurarken üyesine bir şey ekleyemezsek olaylar karışır.

Aslında oldum olası, şu immutable'ı anlayabilmiş değilim. Şimdi bir ayrıntısını daha öğrenmiş olduk; belki de bir şeyi başında immutable yazdığımızda ne demek olduğunu! Çünkü kapsadığı alan çok geniş ve mümkünse parantezli kullanmalı.

''(*)' Bence bu çok saçma, kim eklenemeyen dinamik bir dizi ister ki!'

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
July 19, 2012

Alıntı (acehreli):

>

Yani türler aynı:

> void main()
> {
>     immutable(immutable(int)[]) a;
>     immutable int[] b;
>     assert(typeid(a) == typeid(b));
> }
> ```

>
Evet aynı, a'nın türünü typeid'den çalmıştım...:)

Sanırım sadece başına immutable koymak türü de türün yapısını da immutable yapmakta. Yani derleyicinin auto'su gibi tam otomatik bir şey. Ali hocanın sorularına cevap verecek örnek aklıma gelmiyor. Ama kendi sorduğum soruya cevap belki olabilir. Yani yukarıdaki koda göre b'ye ne zaman ihtiyaç duyarız?

Farz edelim yazılımınız ile ilgili bir takım kritik sabitleri, bir sabit dizide tutmak istiyoruz. Hatta bunların araya ekleme gibi yerlerinin kesinlikle değişmemesi gerekiyor. Öyle ki dizinin uzunluğunun da artması kurduğumuz 'foreach()''li yapıdan dolayı işleri karıştırıyor. Örneğin şöyle:

immutable float[] param = [ 0.9, 3.33, 7.2, 10.1 ] // Toplam 4 parametre


Şimdi biz bunu sabit dizide yapmış olsaydık ('float'[4] param) ve yeni parametreler eklememiz gerekseydi her seferinde eşitiliğin her iki tarafını da düzenlememiz gerekecekti. Tabi enum gibi olanaklar var ama biz diziyle yapacağız ya...:)

Bu arada immutable bize bir olanağı daha sağlıyor. O da parametrelerin her birinin değişmemesini:

immutable float[] param = [ 0.9, 3.33, 7.2, 10.1 ] // Toplam 4 parametre
param[0] = 0; // Derleme hatası...


D'yi seviyorum...:)

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
July 19, 2012

DilimKombinasyonu konusuna devam ederken pis bir hatayla boğuşmak zorunda kaldım. :) Hatayı bulmam ve çözmem çok zamanımı aldı. :(

Aşağıdaki basitleştirilmiş program bu hatayı içeriyor. Program çok basit: main() bir işçi başlatıyor; ona bir Görev gönderiyor ve karşılığında da geriye bir int alıyor.

Ne yazık ki aşağıdaki program takılıp kalıyor (umarım platform farkları beni yalıltmıyordur :)):

import std.stdio;
import std.concurrency;

struct Görev
{
   immutable int[] dilim;
}

void foo(Tid sahip)
{
   receive(
       (Görev görev)
       {
           writeln("İşçi görevi aldı: ", görev);
           sahip.send(42);
       }
   );
}

void main()
{
   auto işçi = spawn(&foo, thisTid);

   immutable int[] dilim = [ 1, 2 ];
   işçi.send(Görev(dilim));

   receive(
       (int sonuç)
       {
           writeln("Sahip sonucu aldı: ", sonuç);
       }
   );
}

Eğer yapacak hiiiç başka işiniz yoksa bu noktada üzerinde çalışmak isteyebilirsiniz. Tabii program çok küçük olduğu için rastgele yerlerini değiştirmek bile ne ile ilgili olduğunu hemen göstermeye yetecektir. Ben çok daha uzun ve karmaşık bir programla boğuşmak zorundaydım. :)

Beni hatanın kaynağına götüren adımlar şunlardı (yararsız adımların çoğuna gerek görmüyorum ;)):

  • Çıkışa "İşçi görevi aldı" yazdırılmadığına göre acaba işçi bir hatayla mı sonlanıyordu? Bunu görmek için spawn() yerine spawnLinked() kullandım:
   auto işçi = spawnLinked(&foo, thisTid);

spawnLinked(), işçi sonlandığında sahip tarafta LinkTerminated hatası atılmasına neden olur. Güzel:

'$ ./deneme
std.concurrency.LinkTerminated@std/concurrency.d(263): Link terminated
'

Dikkat ederseniz "İşçi görevi aldı" yine de yok.

  • İşçiyi bütünüyle bir try-catch bloğu arasına alarak hata ile mi sonuçlandığına bakmak istedim ama onunla hata yakalayamadım. Burada bir hata yapmışım: catch bloğunda Exception türünü kullanmışım.

(Notz: Meğerse hata bir AssertError imiş ve AssertError Exception'dan türemediği için catch(Exception) onu yakalayamamış (AssertError Error'dan türer.) Sonunda Exception ve Error'ın üst türleri olan Throwable türünü catch edince yakalayabildim. Ama bunu aşağıdaki adımlardan sonra farkettim.)

  • İşçiye Görev yerine int göndermeyi denedim; program doğru çalıştı. Acaba receive() Görev türündeki mesajı (Görev görev) türündeki işlev ile ilişkilendiremiyor muydu? Eğer öyleyse receive()'e bir (Variant mesaj) da ekledim. Böylece gönderdiğim Görev nasıl olsa ona takılırdı:
   receive(
       (Görev görev)
       {
           writeln("İşçi görevi aldı: ", görev);
           sahip.send(42);
       },

       (Variant mesaj)
       {
           writeln("Beklenmedik bir mesaj aldım: ", mesaj);
       }
   );

Ne yazık ki aynı hata! :(

  • Bunun üzerine daha önceki catch'teki hatamı farkettim ve Throwable yakalamayı denedim (Not: Normalde bunu yapmayın. Programın normal işleyişi sırasında Exception'dan daha üst türlerin yakalanması önerilmez çünkü yakalandığında programın durumu hakkında pek birşey söylenemez. Örneğin yapıların sonlandırıcı işlevleri işletilmiyor olabilir çünkü runtime bu tür bir hatanın onları çağıramayacak kadar ciddi olduğunu düşünür.)
void foo(Tid sahip)
{
   try {

       // ...

   } catch (Throwable hata) {
       writeln("İşçi hata ile çıkıyor: ", hata);
   }
}

Evet, öyle:

'./deneme
İşçi hata ile çıkıyor: core.exception.AssertError@/usr/include/d/dmd/phobos/std/variant.d(286): Görev'

Sonunda hatanın bir AssertError olduğunu ve variant.d'nin 286 numaralı satırında atıldığını görüyoruz. Tam yolu verilen kaynak kodu açıyorum:

               else
               {
                   // type is not assignable
                   if (src) assert(false, A.stringof);  // <--- 286. satır
               }

Yani tür atanamaz bir türmüş. Nasıl? Hata mesajı o türün Görev olduğunu da söylüyor. Deneyelim:

   auto a = Görev();
   auto b = a;    // <-- Derleniyor

Hani atanamazdı! Ah Ali! :) Yukarıdaki bir atama değil, kopyalayarak kurma. b, a'dan kopyalanıyor. Atama olabilmesi için b'nin önceden var olması gerekir. Deneyelim:

   auto a = Görev();
   auto b = Görev();
   b = a;    // <-- Derleme hatası

Evet, atanamıyor:

'deneme.d(86030): Error: variable deneme.main.b cannot modify struct with immutable members'

Yani immutable üyeler değiştirilemezlermiş. Biliyoruuum... :) Üyeyi öyle seçerek hem kendimi sağlama almışım hem de bir mesaj olarak başka bir iş parçacığına gönderebilmişim. Çünkü asıl programa dönüp iki yerdeki immutable'ı kaldırınca şöyle oluyor:

struct Görev
{
   int[] dilim;    // <-- immutable değil
}

// ...
   int[] dilim = [ 1, 2 ];    // <-- immutable değil
   işçi.send(Görev(dilim));

Derleme hatası:

'/usr/include/d/dmd/phobos/std/concurrency.d(319): Error: static assert "Aliases to mutable thread-local data not allowed."
deneme.d(86031): instantiated from here: send!(Görev)
'

Bunun önüne kilitlerle filan geçilebilir ama onlara girmek istemiyorum. Ben veriyi immutable olarak işaretlemek ve iş parçacıkları arasında serbestçe paylaşmak istiyorum. Değişmez olduğu için hiçbir tehlikesi olamaz.

Sonunda çözümü farkediyorum: Görevin üyesi değil, onun eriştirdiği veri immutable olmalı. Zaten bana yeterli olan da o:

struct Görev
{
   immutable(int)[] dilim;    // <-- Veri immutable ama üye değil
}
// ...
   immutable(int)[] dilim = [ 1, 2 ];
   işçi.send(Görev(dilim));

Sonunda istenen sonuç:

'./deneme
İşçi görevi aldı: Görev([1, 2])
Sahip sonucu aldı: 42'

Kusura bakmayın uzun oldu ama ben bu konudan çok ders aldım. :)

Bu arada, derleyicinin üye için 'immutable int[]' türünü kabul etmemisinin geçerli nedeni var. Görebiliyor musunuz? Benim onu görmem de biraz zaman aldı.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

July 19, 2012

Alıntı (Salih Dinçer):

>

Boyutu da değiştirilemeyen ise immutable(immutable'(int)'[])'' ile dört bir yandan kuşatılıyor...:)
...
''(
)' Bence bu çok saçma, kim eklenemeyen dinamik bir dizi ister ki!'

O zaman buna şaşırabilirsin ama o türler aslında aynı çünkü D'de tür belirteçleri derinlemesinedir. Dilim immutable (veya const) olunca elemanlar da immutable (veya const) oluyor.

Yani türler aynı:

void main()
{
   immutable(immutable(int)[]) a;
   immutable int[] b;
   assert(typeid(a) == typeid(b));
}

Bu arada, tamam, bu işin düzeneğini anlıyoruz ama b a'dan atanabilse ne zararı olurdu? Ona uygun bir örnek düşünebiliyor musunuz? Yani dilin kuralları neden böyle? Zor bir soru değil tabii ama izin verilseydi zararı olabilecek bir örnek görsek? :)

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

July 19, 2012

Aslında bu örneğin konuştuklarımızdan pek bir farkı yok. r0, r1, ve r2 referansları S.dilim'in immutable olmasına güveniyorlar. Eğer b'ye atamaya izin verilseydi durum onların sandıklarından farklı olurdu:

struct S
{
   immutable int[] dilim;
}

void main()
{
   auto b = S();

   foo(b);    // <-- dolaylı olarak r0'ı ayarlıyor
   auto r1 = &b.dilim;
   auto r2 = b.dilim.length;

   auto a = S();
   // Buna izin verilseydi r0, r1, ve r2 aldanmış olurlardı.
   // b = a;
}

immutable(int[]) * r0;

void foo(ref S s)
{
   r0 = &s.dilim;
}

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]