C# ile Paralel Programlama-3 (Eş Zamanlı Koleksiyonlar)

İlker Erhalim
3 min readJan 7, 2021
Yığını temsil eden üstüste konulmuş bozuk paralar ve arka planda bir saat olan görsel.
Pixabay adlı kişinin Pexels’daki fotoğrafı

Single thread bir uygulamada çalışıyorken, bir koleksiyonu yönetmek oldukça kolaydır. Ancak paralel bir uygulamada bir koleksiyon ile çalışırken hangi taskin önce müdahale edeceğini bilmiyoruz. Bu tarz durumlarda taskler arası veri paylaşımından bahsettiğim bu yazımdaki yöntemlerden herhangi biri kullanılabilineceği gibi System.Collections.Concurent namespace altındaki Enumarable implementasyonları da kullanılabilinir.

En çok kullanılan koleksiyon olduğu için problemi Generic List üzerinden anlatmak istiyorum. Normal şartlarda bir liste üzerine bir kayıt eklediğinizde kayıt listenin sonuna eklenir ve bu kayıda erişmenin birden fazla yolu vardır. Bunlardan bir tanesi Enumarable (System.Linq namespace) sınıfındaki Last ya da LastOrDefault extensionlarını kullanmaktır. Ancak bir listeye farklı taskler üzerinden kayıt ekliyorsanız son kaydın hangi thread üzerinden eklendiğini bilmeniz mümkün değil.

Normal şartlarda yukarıdaki örneği çalıştırdığınızda her bir iterasyon için result copyOfi değişkenine eşit olmalı. Ancak bu senaryoda listeye aynı anda müdahale eden beş farklı task var. Bu taskler çalışmak için birbirini beklemiyor. Uygulama her çalıştığında sonuç farklı olacaktır.

Ben çalıştırdığımda aldığım çıktı;

Task 4 Add 4
Task 1 Add 1
Task 2 Add 2
Task 3 Add 3
Task 4 Last record 3
Task 3 Last record 3
Task 1 Last record 3
Task 2 Last record 3

Bu çıktıya bakarak uygulamanın nasıl çalıştığını anlamak mümkün;

Tüm taskler aynı anda çalışmaya başlamış ve herhangi biri 22. satıra gelmeden önce ekleme işlemlerinin tamamı yapılmış. En son tamamlanan task üçüncü task olduğu için Last extensionı tüm kayıtlar için 3 değerini döndürmüş.

ConcurentBag

Eş zamanlı çalışan (Concurent) koleksiyonlar arasında en çok kullanılandır, adının “Bag” olma sebebi eklenen kayıtları sırasız bir şekilde (bir çantaya doldurur gibi) tutmasıdır.

Eş zamanlı koleksiyonlar kullanılırken dikkat edilmesi gereken noktalardan biri herhangi bir Extension metot kullanmamaktır. Eş zamanlı olan koleksiyondur, Extensionlar değil.

Yukarıdaki gibi bir implementasyon yapıldığında sağlıklı bir çıktı alabiliriz.

Task 1 Add 1
Task 4 Add 4
Task 2 Add 2
Task 3 Add 3
Task 2 Last record 2
Task 1 Last record 1
Task 3 Last record 3
Task 4 Last record 4

ConcurentDictionary

Dictionary koleksiyonunun özeliklerine ek olarak daha kontrollü çalışmamamıza olanak sağlayan bir Enumerabledır.

Kontrollü çalışmamıza olanak sağlayan bazı methodlar;

  • GetOrAdd: Dictionary içerisinde verilen keyin değeri varsa döndürür, yoksa vakue veya valueFactory üzerinden dictionarye ekleyip geriye yeni değeri döndürür.
  • TryRemove: Verilen key dictionary içinde tanımlıysa siler, value parametresini, silinen keyin değeri olarak set eder. Değer silindiyse true, silinmediyse (zaten yoksa) false döndürür.
  • AddOrUpdate: Verilen key dictionary sınıfında yoksa value parametresindeki değeri dictionarye ekler, varsa valueFactory içerisinden dönen değer olarak update eder.

Yukarıdaki örneği çalıştırdığınızda çıktı aşağıdaki gibi olacaktır;

GetOrAddExample
Value is: Isim
Value is: Isim
TryRemoveExample
WillBeRemove is removed, the old value was: Silinecek
Trying to remove same key again
Could not remove the key, it's allready removed.
AddOrUpdateExample
Lastname value is: Soyad
Value of 'Lastname' is Soyad but it will be updated to Soyisim
Lastname value is: Soyisim

ConcurentQueue

ConcurentDictionary’i okuduktan sonra ConcurentQueue’nun ne olduğunu tahmin edebiliyorsunuzdur. Normal Queue’dan farklı olarak ConcurentQueue’da Peek ve Enqueue metotları yerine TryPeek ve TryEnqueue metotları vardır.

ConcurrentStack

ConcurentStack koleksiyonu da Stack koleksiyonunun metotlarının Try ile implement edilmiş halidir. Peek, Pop, PopRange metotları yerine TryPeek, TryPop, TryPopRange metotları kullanılabilinir.

--

--