Merhaba, bu yazımda QT threadlerini QT dokümanlarından faydalanarak anlatmaya çalışacağım. Bu konudaki yazılarım genel olarak anlatım kısmında qt.io dokümanlarını içermektedir. Bu dokümanları burada türkçeye çevirerek, yorum katarak ve kod örnekleri üzerinden daha iyi bir şekilde anlatmayı planlıyorum, iyi okumalar.

Başlamadan önce threadler hakkında temel bilgi için: https://doc.qt.io/qt-6/thread-basics.html

Qt, platformdan bağımsız thread sınıfları, olayları göndermenin thread açısından güvenli bir yolu ve threadler arasında sinyal yuvası bağlantıları şeklinde iş parçacığı desteği sağlar. Bu, taşınabilir multithread Qt uygulamaları geliştirmeyi ve çok işlemcili makinelerden yararlanmayı kolaylaştırır. Multithread programlama, bir uygulamanın kullanıcı arayüzünü dondurmadan zaman alan işlemleri gerçekleştirmek için de yararlı bir paradigmadır.

Konu Başlıkları:

  • Thread Sınıfları
  • Qt’de Multithreading Teknolojileri
  • Thread’leri Senkronize Etme
  • Reentrancy ve Thread Güvenliği
  • Thread’ler ve QObject’ler
  • Qt Modüllerinde Thread Desteği

Thread Sınıfları

Qt, çoklu iş parçacığı programlamasını destekler ve bu amaçla bir dizi threading sınıfı sunar. Qt’nin threading sınıfları, çoklu iş parçacığı programlamasını daha kolay hale getirmek ve çeşitli iş parçacığı senaryolarını ele almak için tasarlanmıştır.

İşte Qt’de kullanılan bazı threading sınıflarının örnekleri:

  1. QThread: Qt’de threading yapmanın temel yoludur. QThread sınıfı, bir iş parçacığını temsil eder ve yeni iş parçacıkları oluşturmak, mevcut iş parçacıklarına katılmak veya iş parçacıklarını kontrol etmek için kullanılabilir.
  2. QMutex: Çoklu iş parçacığı programlamasında kritik bölgeleri korumak için kullanılır. Birden çok iş parçacığı arasında güvenli bir şekilde veri paylaşımını sağlamak için mutexler kullanılır.
  3. QMutexLocker: QMutex nesnelerinin kilitlenmesini ve kilidin otomatik olarak açılmasını sağlar. Bu, mutex kilidinin unutulması gibi hataları önler ve kodun daha temiz ve güvenli olmasını sağlar.
  4. QWaitCondition: İş parçacıklarının bir koşula bağlı olarak uyumasını sağlar. Bir iş parçacığı, belirli bir koşulun gerçekleşmesini beklerken uyumak istiyorsa, QWaitCondition kullanılabilir.


Thread sınıflarının tamamı için linkteki listeye göz atabilirsiniz
https://doc.qt.io/qt-6/threads.html#the-threading-classes

Qt’de Multithreading Teknolojileri

Qt, threadler ile çalışmak için birçok sınıf ve fonksiyon sunar. Aşağıda, Qt programcılarının multithread uygulamaları uygulamak için kullanabilecekleri dört farklı yaklaşım yer almaktadır.

1- QThread: İsteğe Bağlı Olay Döngüleriyle Düşük Seviyeli API
(Low-Level API with Optional Event Loops)

QThread, Qt’deki tüm iş parçacığı kontrolünün temelidir. Her QThread örneği bir iş parçacığını temsil eder ve kontrol eder.

QThread doğrudan örneklenebilir ya da alt sınıflanabilir. Bir QThread ‘in örneklenmesi, QObject yuvalarının ikincil bir iş parçacığında çağrılmasına izin veren paralel bir olay döngüsü sağlar. Bir QThread ‘i alt sınıflamak, uygulamanın olay döngüsünü başlatmadan önce yeni iş parçacığını başlatmasına veya olay döngüsü olmadan paralel kod çalıştırmasına izin verir.

2- QThreadPool and QRunnable: Thread’leri Yeniden Kullanma

Sık sık iş parçacığı oluşturmak ve yok etmek pahalı olabilir. Bu ek yükü azaltmak için, mevcut iş parçacıkları yeni görevler için yeniden kullanılabilir. QThreadPool, yeniden kullanılabilir QThread’lerin bir koleksiyonudur.

Bir QThreadPool‘un iş parçacıklarından birinde kod çalıştırmak için, QRunnable::run()’ı yeniden uygulayın ve alt sınıflı QRunnable‘ı örnekleyin. QRunnable ‘ı QThreadPool’un çalıştırma kuyruğuna koymak için QThreadPool ::start() kullanın. Bir iş parçacığı kullanılabilir hale geldiğinde, QRunnable::run() içindeki kod o iş parçacığında çalışacaktır.

Her Qt uygulaması, QThreadPool::globalInstance() aracılığıyla erişilebilen global bir iş parçacığı havuzuna sahiptir. Bu global iş parçacığı havuzu, CPU’daki çekirdek sayısına bağlı olarak otomatik olarak en uygun iş parçacığı sayısını korur. Bununla birlikte, ayrı bir QThreadPool oluşturulabilir ve açıkça yönetilebilir.

3- Qt Concurrent: Üst Düzey API Kullanımı

Qt Concurrent modülü, bazı yaygın paralel hesaplama modelleriyle ilgilenen yüksek seviyeli fonksiyonlar sağlar: map, filter ve reduce. QThread ve QRunnable kullanımının aksine, bu fonksiyonlar muteksler veya semaforlar gibi düşük seviyeli iş parçacığı ilkellerinin kullanımını asla gerektirmez. Bunun yerine, hazır olduklarında fonksiyonların sonuçlarını almak için kullanılabilecek bir QFuture nesnesi döndürürler. QFuture ayrıca hesaplama ilerlemesini sorgulamak ve hesaplamayı duraklatmak/devam ettirmek/iptal etmek için de kullanılabilir. Kolaylık sağlamak için QFutureWatcher, QFuture‘lar ile sinyaller ve slotlar aracılığıyla etkileşim sağlar.

Qt Concurrent‘ın eşleme, filtreleme ve azaltma algoritmaları, hesaplamayı mevcut tüm işlemci çekirdeklerine otomatik olarak dağıtır, böylece bugün yazılan uygulamalar daha sonra daha fazla çekirdeğe sahip bir sisteme dağıtıldığında ölçeklenmeye devam eder.

Bu modül ayrıca herhangi bir fonksiyonu başka bir iş parçacığında çalıştırabilen QtConcurrent::run() fonksiyonunu da sağlar. Bununla birlikte, QtConcurrent::run() sadece map, filter ve reduce fonksiyonları için mevcut olan özelliklerin bir alt kümesini destekler. QFuture, fonksiyonun geri dönüş değerini almak ve iş parçacığının çalışıp çalışmadığını kontrol etmek için kullanılabilir. Ancak, QtConcurrent::run() çağrısı sadece bir iş parçacığı kullanır, duraklatılamaz/devam ettirilemez/iptal edilemez ve ilerleme için sorgulanamaz.

4- WorkerScript: QML’de Thread Oluşturma

WorkerScript QML türü, JavaScript kodunun GUI iş parçacığı ile paralel olarak çalışmasını sağlar.

Her WorkerScript örneği, kendisine eklenmiş bir .js komut dosyasına sahip olabilir. WorkerScript .sendMessage() çağrıldığında, kod ayrı bir iş parçacığında (ve ayrı bir QML bağlamında) çalışır. Kod çalışmayı bitirdiğinde, WorkerScript.onMessage() sinyal işleyicisini çağıracak olan GUI iş parçacığına bir yanıt gönderebilir.

Bir WorkerScript kullanmak, başka bir iş parçacığına taşınmış bir çalışan QObject kullanmaya benzer. Veri, iş parçacıkları arasında sinyaller aracılığıyla aktarılır.

Uygun Bir Yaklaşım Seçmek

Yukarıda gösterildiği gibi, Qt, iş parçacıklı uygulamalar geliştirmek için farklı çözümler sunar. Belirli bir uygulama için doğru çözüm, yeni iş parçacığının amacına ve iş parçacığının yaşam süresine bağlıdır. Aşağıda Qt’nin iş parçacığı teknolojilerinin bir karşılaştırması ve ardından bazı örnek kullanım durumları için önerilen çözümler yer almaktadır.

En düşük seviyede QThread kullanabiliriz ve bu bize hayal edebileceğimiz tüm esnekliği sağlar. Threadleri başlatabilir, bitirebilir, sinyaller ve yuvalar kullanarak arka plan threadinden kullanıcı arayüzü threadine geri bildirim gönderebiliriz. Kısaca her türlü şeyi yapabiliriz.

Bir seviye yukarı çıkarsak, thread havuzlarını göreceğiz, thread havuzları işleri biraz daha kolaylaştıracak çünkü threadleri manuel olarak başlatmak zorunda değilsiniz.
Tek yapmanız gereken işleminizi bir QRunnable iletmek ve bu runnable’ı QThreadPool göndermek.
Ayrıca, QMeta nesne sınıfından post event yöntemini veya invoke yöntem işlevini kullanarak thread havuzlarından kullanıcı arayüzü threadine geri bildirim göndermeyi sağlayabiliriz.

Bir seviye daha yukarı çıkarsak, Qt Concurrent’i göreceğiz,
bu işleri daha da kolaylaştıracaktır çünkü tek yapmanız gereken bir arka plan threadinde çalıştırmak istediğiniz metodu belirtmektir ve run yöntemini kullanarak metodu başlatabilirsiniz.

Şimdi, tüm bu teknolojileri bildiğimize göre, uygulamalarımızda threadlere ihtiyaç duyarsak hangisini kullanacağınızı sorabilirsiniz.
Burada öncelikle gerçekten kendi araştırmanızı yapmalı ve çözmeye çalıştığınız sorunun ne olduğunu görmelisiniz.
Ancak genel bir kural olarak, uygulamalarımızda thread kullanmak istediğimizde, mümkün olan en yüksek seviyeden başlayıp aşağıya doğru inmeliyiz.

Bu yüzden sorunu ilk olarak Qt Concurrent kullanarak çözmeye çalışmalıyız. Çalıştırmaya çalıştığımız sadece bir yöntemse, sorunu çözmek için map işlevlerini kullanmakla başlayabiliriz, Eğer çözmeye çalıştığımız sorunu çözmeyi kolaylaştıracaksa filter ve reduce yöntemlerine de başvurabiliriz.

Qt Concurrent yardımcı olamazsa, QThreadPool kullanmayı deneriz ve QThreadPool kullanamazsak son olarak QThread kullanarak iş parçacıklarını kendimiz başlatıp yöneteceğiz.

Eğer sonuna kadar gider ve QThread kullanırsanız, bir olay döngüsüne ihtiyacınız olup olmadığını gerçekten netleştirmeniz gerekir.
Eğer bir olay döngüsüne ihtiyacınız varsa, gerçekten iki seçeneğiniz vardır. Bir nesneyi alıp move to thread kullanarak başka bir iş parçacığına taşıyabilirsiniz, bu da size varsayılan olarak bir olay döngüsü sağlayacaktır. Ya da QThread alt sınıfını oluşturabilir ve exec yöntemini çağırabilirsiniz.

Çözümlerin Karşılaştırılması

ÖzellikQThreadQRunnable ve QThreadPoolQtConcurrent::run()Qt Concurrent (Map, Filter, Reduce)WorkerScript
DilC++C++C++C++QML
Thread önceliği belirlenebilirEvetEvet
Thread bir olay döngüsünü (event lop) çalıştırabilirEvet
Thread sinyaller aracılığıyla veri güncellemelerini alabilirEvet(Worker QObject tarafından alınır)Evet(WorkerScript tarafından alınır)
Thread sinyaller kullanılarak kontrol edilebilirEvet(QThread tarafından alınır)Evet(QFutureWatcher tarafından alınır)
Thread QFuture aracılığıyla izlenebilirKısmenEvet
Duraklatma/devam ettirme/iptal etme yerleşik yeteneğiEvet

Örnek Kullanım Durumları

Thread’in ömrüOperasyonÇözüm
Tek çağrıBaşka bir thread içinde, isteğe bağlı olarak çalışma sırasında ilerleme güncellemeleriyle yeni bir doğrusal işlev çalıştırın.Qt farklı çözümler sunar:

Fonksiyonu QThread::run()’ın bir yeniden uygulamasına yerleştirin ve QThread‘i başlatın. İlerlemeyi güncellemek için sinyaller yayın. Veya

Fonksiyonu QRunnable::run()’ın bir yeniden uygulamasına yerleştirin ve QRunnable ‘ı bir QThreadPool‘a ekleyin. İlerlemeyi güncellemek için iş parçacığı güvenli bir değişkene yazın. Veya

QtConcurrent::run() kullanarak fonksiyonu çalıştırın. İlerlemeyi güncellemek için iş parçacığı güvenli bir değişkene yazın.
Tek çağrıMevcut bir işlevi başka bir thread içinde çalıştırın ve dönüş değerini alın.Fonksiyonu QtConcurrent::run() kullanarak çalıştırın. Fonksiyon geri döndüğünde bir QFutureWatcher’ın Finish() sinyalini göndermesini sağlayın ve fonksiyonun dönüş değerini almak için QFutureWatcher::result()’u çağırın.
Tek çağrıMevcut tüm çekirdekleri kullanarak bir kapsayıcının tüm öğeleri üzerinde bir işlem gerçekleştirin. Örneğin, bir resim listesinden küçük resimler üretmek.Konteyner elemanlarını seçmek için Qt Concurrent’ın QtConcurrent ::filter() fonksiyonunu ve her elemana bir işlem uygulamak için QtConcurrent:: map() fonksiyonunu kullanın. Çıktıyı tek bir sonuca katlamak için, bunun yerine QtConcurrent ::filteredReduced() ve QtConcurrent: : mappedReduced() fonksiyonlarını kullanın.
Tek çağrı/KalıcıSaf bir QML uygulamasında uzun bir hesaplama gerçekleştirin ve sonuçlar hazır olduğunda GUI’yi güncelleyin.Hesaplama kodunu bir .js betiğine yerleştirin ve bir WorkerScript örneğine ekleyin. Hesaplamayı yeni bir iş parçacığında başlatmak için WorkerScript .sendMessage() işlevini çağırın. Sonucu GUI iş parçacığına geri iletmek için kodun sendMessage() işlevini de çağırmasına izin verin. Sonucu onMessage ‘da işleyin ve GUI’yi burada güncelleyin.
KalıcıBaşka bir threadde yaşayan ve istek üzerine farklı görevleri yerine getirebilen ve/veya üzerinde çalışmak için yeni veriler alabilen bir nesneye sahip olun.Bir çalışan oluşturmak için bir QObject’i alt sınıflayın. Bu çalışan nesnesini ve bir QThread’i somutlaştırın. Çalışanı yeni iş parçacığına taşıyın. Kuyruktaki sinyal yuvası bağlantıları üzerinden çalışan nesneye komutlar veya veriler gönderin..
KalıcıThreadin herhangi bir sinyal veya olay alması gerekmediği başka bir threadde pahalı bir işlemi tekrar tekrar gerçekleştirin.Sonsuz döngüyü doğrudan QThread::run()’un bir yeniden uygulaması içine yazın. Olay döngüsü olmadan iş parçacığını başlatın. İş parçacığının GUI iş parçacığına geri veri göndermek için sinyaller yaymasına izin verin.

Buraya kadar okuduğunuz için teşekkürler, sonraki yazıda yukarıda bahsettiğim 4 multithreading teknolojisi hakkında birer örnek kod vererek pratiktede konuyu daha iyi anlamaya çalışacağız, daha sonra kalan başlıklar üzerinden ilerlemeye devam edeceğiz, iyi günler.

Kaynak

https://doc.qt.io/qt-6/threads-technologies.html

Yorum bırakın

Trend

WordPress.com’da Blog Oluşturun.