logologo
Ai badge logo

Bu madde yapay zeka desteği ile üretilmiştir.

Qt İle İş Parçacığı Yönetimi ve Paralel Programlama

Savunma Sanayi Teknolojileri+2 Daha
fav gif
Kaydet
viki star outline

Modern GUI uygulamalarında zaman alıcı işlemler arayüzün donmasına neden olabilir. Bu nedenle Qt gibi uygulama çerçeveleri, uzun süren görevleri ana iş parçacığından ayırarak gerçekleştirmek için iş parçacıkları ve eşzamanlı (paralel) programlama araçları sağlar. Bir Qt uygulamasında iş parçacığı yönetimi, hem çok çekirdekli işlemcileri etkin kullanarak performansı artırmak hem de kullanıcı arayüzünün tepki süresini korumak açısından kritiktir. Çok çekirdekli donanımlarda birden fazla iş parçacığı paralel çalışarak hesaplamayı hızlandırırken, ağır işlemler arka plandaki iş parçacıklarında yürütülür; böylece ana iş parçacığı GUI’nin tepki verişini kesintiye uğratmaz.


İş Parçacıkları Ve Objeleri Oluşturma(Qt İş Parçacığı Ve Objeleri Tanımlama)


Qt uygulamalarında her iş parçacığının kendine ait bir yığını ve program sayacı bulunur; kod ve veri alanı ise tüm iş parçacıkları arasında paylaşılır. Yukarıdaki şekilde görüldüğü gibi, bir “ana iş parçacığı” (GUI iş parçacığı) ve bir “yardımcı iş parçacığı” aynı işlemci çekirdeği üzerinde sırasıyla çalıştırılabilir. Qt’de her program başlatıldığında bir ana iş parçacığı (GUI thread) oluşur ve tüm widget’lar bu iş parçacığında çalışır. Yardımcı (worker) iş parçacıkları ise arka planda zaman alan görevleri üstlenir. Örneğin bir hesaplama işini gerçekleştiren yardımcı iş parçacığı çalışırken, ana iş parçacığı kullanıcı arayüzünü güncellemeye devam edebilir. Bu düzenleme sayesinde zaman alan işlemler sırasında bile kullanıcı arayüzü donmaz ve uygulama akıcı kalır.

QThread Sınıfı

Qt’de iş parçacığı denetiminin temeli QThread sınıfıdır. Her QThread örneği, program içinde bağımsız bir iş parçacığını temsil eder ve kontrol eder. QThread ya doğrudan örneklenebilir (örneğin bir QThread nesnesi oluşturup bir worker nesnesini bu iş parçacığında çalışacak şekilde ayarlayabiliriz) ya da alt sınıfı çıkarılarak run() fonksiyonu ezilebilir. QThread örneği örneklenip çalıştırıldığında, varsayılan olarak bir olay döngüsü başlatır ve bu döngü sayesinde bağlantı (signal-slot) mekanizması iş parçacığında sorunsuz çalışır. QThread alt sınıfı ise yeni iş parçacığı başlatılmadan önce başlatılabilir ve run() içinde doğrudan paralel kod çalıştırılabilir.


QThread kullanmanın avantajlarından biri, Qt’nin olay döngüsü ve sinyal-slot altyapısı ile uyumlu olmasıdır. Bu sayede bir iş parçacığı içinde çalışan QObject’lar, diğer iş parçacılarından sinyal-slot ile iletişim kurabilir. Ayrıca QThread, başka bir iş parçacığından gelen işlemleri sıraya alarak güvenli bir şekilde çalışmayı sağlar. Örneğin, bir hesaplama işlevini arka planda çalıştırmak için bir QObject sınıfını tanımlayıp bu nesneyi moveToThread() ile QThread’e taşıyabiliriz.


Akademik uygulama örneği olarak, Aldinucci ve arkadaşlarının【1】  Mandelbrot seti uygulamasında QThread kullanımı gösterilebilir. Bu uygulamada, RenderThread adında bir QThread alt sınıfı fractal hesaplamayı yaparken, MandelbrotWidget adında bir QWidget alt sınıfı kullanıcıya görüntüyü sunar. RenderThread yardımcı iş parçacığı fractal hesaplama işini yürütürken, ana iş parçacığı daha önce hesaplanmış görüntüyü ölçeklendirerek ekranda gösterir. Bu sayede hesaplama sırasında bile kullanıcı arayüzü yanıt vermeyi sürdürür.

moveToThread Metodu

QObject’ler başlangıçta oluşturuldukları iş parçacığına bağımlıdır. moveToThread() yöntemi, bir QObject’un (ve onun alt nesnelerinin) çalışacağı iş parçacığını değiştirmek için kullanılır. Bu yöntem çağrıldığında, nesne ve çocukları hedef iş parçacığına taşınır ve olay döngüsü o iş parçacığında devam eder. Bununla birlikte, nesnenin bir üst sınıfı (parent) varsa moveToThread() başarısız olur.Bir başka deyişle, tüm QObject’lar üst sınıfları ile aynı iş parçacığında yaşamalıdır. Ayrıca moveToThread() mevcut iş parçacığı içinde çağrılmalı ve eğer hedef iş parçacığı nullptr ise nesne hiçbir iş parçacığına bağlı olmaz (yani olay işleme durur)


moveToThread kullanılırken dikkat edilmesi gereken ana noktalar şunlardır:

  • Parent olmaması: Taşınacak QObject’un üst nesnesi olmamalıdır (parent=NULL olmalıdır)
  • Doğru konumdan çağrı: moveToThread() metodu, nesnenin şu an bağlı olduğu iş parçacığından çağrılmalıdır. Aksi halde beklenmeyen sonuçlar doğabilir.


Bu yöntem tipik olarak, uzun süren işlemleri gerçekleştiren iş nesnelerinin ilgili QThread içerisinde çalıştırılması için kullanılır. Örneğin bir seri port veya CAN iletişimi uygulamasında bir worker QObject oluşturulup ilgili QThread’e taşınarak arka planda veri akışı sağlanabilir. Bu sayede, veri işleme iş parçacığında devam ederken ana iş parçacığı kullanıcı arayüzünü güncellemeye devam eder.

QtConcurrent

QtConcurrent modülü, Qt’ye gömülü yüksek seviyeli paralel çalışma araçları sunar. Map, filter, reduce gibi ortak hesaplama şablonlarını destekleyen fonksiyonlar içerir. Bu fonksiyonlar, arkada düşük seviye kilit mekanizmalarıyla uğraşmak yerine otomatik olarak işlemci çekirdekleri arasında işi paylaştırır. Örneğin QtConcurrent::map() bir veri kümesinin her elemanını paralel olarak işleyebilir. Bu fonksiyonlar, CPU üzerindeki çekirdek sayısına göre otomatik ölçeklenebilirlik sağlar.


QtConcurrent kullandığımızda QFuture nesnesiyle hesaplamanın sonucu ve ilerlemesi takip edilebilir. QtConcurrent::run() ise tek bir işlevi arka planda çalıştırıp QFuture döndürür; bu sayede geri çağrım veya sonucun kontrolü kolaylaşır. Ancak QtConcurrent::run() bazı kısıtlamalara sahiptir (örneğin çalışmayı durdurma, ilerlemeyi sorgulama gibi özellikleri sınırlıdır.) Ayrıca QtConcurrent, sürekli iletişim veya uzun süren engelleyici işlemler için ideal değildir; bu tür durumlarda sinyal/slot temelli QThread kullanımı tercih edilir.


Akademik literatürde de QtConcurrent’in kullanıldığı çalışmalara rastlanır. Örneğin Hossam ve arkadaşları (2013), hiyerarşik görüntü segmentasyonu algoritmasının çok çekirdekli CPU kısmını QtConcurrent ile paralelleştirmiştir. Bu çalışmada QtConcurrent ve QtNetwork kütüphaneleri kullanılarak CPU hesaplamaları yüksek eşzamanlılıkla gerçekleştirilmiştir. Bu tür yüksek seviyeli API’ler, geliştiricinin paralel programlamayı kolayca uygulamasına olanak tanır.

Reentrant ve Thread-Safe Kavramları

Qt dokümantasyonuna göre thread-safe bir fonksiyon, eşzamanlı olarak birden fazla iş parçacığından çağrıldığında paylaşılan verilere erişimleri sıraya koyarak güvenli çalışır. Reentrant bir fonksiyon ise aynı anda birden fazla iş parçacığından çağrılsa da her çağrı için farklı veri kullanılması koşuluyla güvenlidir. Yani, bir fonksiyon thread-safe ise mutlaka reentrant’dir; ancak reentrant bir fonksiyon her zaman thread-safe olmayabilir. Bunun sınıf düzeyinde karşılığı, bir sınıfın reentrant sayılabilmesi için her iş parçacığının kendi örneğini kullanması yeterliyken, thread-safe bir sınıf aynı örneğin tüm iş parçacıkları tarafından aynı anda güvenle kullanılabilmesini gerektirir. Qt belgeleri, eğer bir sınıf veya fonksiyon açıkça reentrant veya thread-safe olarak işaretlenmemişse, farklı iş parçacıklarından kullanmamak gerektiğine özellikle dikkat çeker.

  • Thread-safe fonksiyon: Çoklu iş parçacığı tarafından aynı anda çağrıldığında, paylaşılan veriler üzerinde erişimler kilitlenerek veya sıralanarak güvenliği sağlayan fonksiyon
  • Reentrant fonksiyon: Aynı anda birden fazla iş parçacığı tarafından çağrılabildiği halde her çağrı için farklı veri kullanan fonksiyon. Farklı iş parçacıkları aynı anda aynı örneği kullanmadığı sürece güvenlidir.


C++ sınıfları genellikle kendi üye değişkenlerini kullandıkları için reentrant olurlar: Her iş parçacığı kendi sınıf örneği üzerinde çalıştığında başka bir iş parçacığı aynı örneği kullanmaz ise güvenli çalışır. Örneğin Qt dökümantasyonunda verilen Counter sınıfı, her iş parçacığının kendine ait Counter örneğini kullanması durumunda reentrant kabul edilir, fakat örneğin aynı anda birden çok iş parçacığı increment() fonksiyonunu çağırmaya çalışırsa işlem belirsiz olacağından thread-safe değildir. Gerçekten, Qt belgeleri birçok sınıfı reentrant ama thread-safe olmayan şekilde tasarlamıştır; böylece sık sık mutex kilidi koymanın getireceği maliyetten kaçınılır.


Örneğin QString reentrant bir sınıftır (yani her iş parçacığı kendi QString örneğini kullanabilir), ancak aynı QString örneğine eşzamanlı erişim thread-safe değildir. Benzer şekilde, QList veya QVector gibi implicit sharing (kopya-üzerine-yazma) kullanan konteyner sınıfları da her iş parçacığının kendi kopyasını güvenle kullanabileceği şekilde reentrant’tir. Qt’te kullanılan atomik sayaçlar, her iş parçacığının ayrı bir örneği kullanırken referans sayacını güvenli tutar. ancak aynı nesneye birden fazla iş parçacığından erişilmesi gerekiyorsa geliştirici ek senkronizasyon (örneğin QMutex) eklemelidir.


  • Reentrant (Yeniden-girişimli) sınıflar (ör. QString, QList vb.): Implicit sharing kullanan (kopya-üzerine-yazma) sınıflar tam anlamıyla reentrant olarak kabul edilir. Her iş parçacığı kendi nesne kopyası üzerinde çalıştığı sürece güvenlidir.
  • Ancak aynı nesneye eşzamanlı erişimler thread-safe olmadığından, gerekirse geliştirici tarafından kilit (QMutex) ile korunmalıdır.
  • Thread-safe sınıflar (ör. QMutex, QSemaphore, QWaitCondition vb.): Qt’in özellikle çok parçacıklı programlama için tasarlanmış sınıfları doğaları gereği thread-safe olacak şekilde belgelenmiştir.
  • Örneğin QMutex, QSemaphore, QWaitCondition, QThreadStorage ve QAtomicInteger gibi sınıflar, çoklu iş parçacığı erişimini yerel kilitleme veya atomik işlemlerle senkronize eder.
  • Ayrıca QObject::connect/disconnect, QCoreApplication::postEvent() gibi bazı temel fonksiyonlar da thread-safe olarak tanımlanmıştır.


Qt çerçevesinde bu kavramlar, bir sınıfın belgelenme şekliyle netleşir: Sınıf dökümantasyonunda thread-safe veya reentrant ibaresi yoksa aynı örneğe farklı iş parçacıklarından erişilmemelidir. Örneğin, GUI widget’ları (QWidget ve alt sınıfları) hem reentrant hem de thread-safe değildir; bu nedenle yalnızca ana (GUI) iş parçacığından kullanılmalıdır. Böylece, Qt geliştiricileri genellikle reentrant özelliklere güvenerek, verileri iş parçacıkları arası taşımayı sinyal-slot mekanizması gibi thread-safe altyapılarla gerçekleştirir. Bir iş parçacığı başka bir iş parçacığının objesine veri gönderecekse, nesneler arası ileti Qt’nin güvenli event/sinyal kuyruğu ile yapılır. Binoy Kurikaparambil Revi (2019), bir uygulamalı çalışmada çok parçacıklı bir durum makinesi (state machine) tasarlamış ve Qt kullanarak bu makineyi ayrı bir iş parçacığı üzerinde çalıştırmıştır. Yazar, Qt’nin sinyal-slot mekanizmasını iş parçacıkları arası veri iletimi için kritik bir özellik olarak vurgulamıştır; “Qt, iş parçacığından ana iş parçacığına veri iletimi için mükemmel bir bloklamayan sinyal-slot mekanizması sunar." Gerçekte, Revi’nin uygulamasında her bir iş parçacığı kendine ait bir QStateMachine nesnesi çalıştırmakta ve aralarındaki veri aktarımı signal-slot ile güvenle yapılmaktadır. Bu örnek, Qt’nin reentrant sınıf tasarımının yanında, thread-safe sinyal-slot altyapısını kullanarak paralel programlamayı nasıl kolaylaştırdığını gösterir.

GUI ile Etkileşim

Qt mimarisinde tüm GUI işlemleri ana iş parçacığında yapılmalıdır. Başka bir iş parçacığından GUI nesnelerine doğrudan erişmek güvenli değildir ve uygulamanın çökmesine yol açabilir. Bu kural, akademik çalışmalarda da gözetilir. Örneğin yukarıda bahsedilen Mandelbrot uygulamasında, ağır hesaplama RenderThread iş parçacığında yürütülürken, kullanıcıya gösterilen resim ölçeklendirme işlemleri ana iş parçacığında yapılmıştır. Yani iş parçacıkları arasındaki iletişim sinyal/slot mekanizması ile gerçekleştirilmiş ve GUI güncellemeleri ana iş parçacığına bırakılmıştır. Bu sayede hesaplama süresince GUI’in yanıtlılığı korunmuştur.

Performans ve Senkronizasyon

Qt, iş parçacıkları arasında paylaşılabilen kaynaklara erişimi kontrol etmek için çeşitli senkronizasyon sınıfları sunar. Örneğin QMutex, tek seferde yalnızca bir iş parçacığının kritik bölgeye girmesini sağlayarak veri yarışlarını önler. QSemaphore ise belirli sayıda iş parçacığının aynı anda belirli bir kaynağa erişimine izin verir. Üretici-tüketici (producer-consumer) senaryosunda, QSemaphore kullanılarak hem üreticinin hem de tüketicinin aynı anda farklı tampon bölümlerine erişmesi mümkün olur; oysa QMutex kullanılsaydı tüketici, üretici iş parçacığının yazdığı kısma erişemezdi. Qt dokümantasyonundaki üretici-tüketici örneğinde de QSemaphore, paralelite düzeyini artırmak için tercih edilmiştir. Ayrıca QReadWriteLock gibi sınıflar, okuma-ağırlıklı senaryolarda performansı artırmak için okuma ve yazma erişimini ayrı yönetebilir.


Bu yapılar, farklı senaryolarda performans testlerine tabi tutulur. Örneğin bazı çalışmalarda QMutex ve QSemaphore’un farklı yoğunluklardaki eşzamanlı işlemlerdeki gecikme performansı karşılaştırılmıştır. Genel olarak QtConcurrent gibi soyutlama katmanları bu detayları geliştiriciden gizlerken, QMutex/QSemaphore gibi ilkel yapılar gerektiğinde manuel kontrol imkânı verir. Parçacıklar arası senkronizasyonun dikkatle tasarlanması, performansın artmasına ve uygulamanın doğru çalışmasına katkı sağlar.

Kaynakça

Aldinucci, Marco, Marco Danelutto, Peter Kilpatrick, Massimiliano Meneghin, and Massimo Torquati. “Accelerating Sequential Programs Using FastFlow and Self-Offloading.” arXiv, arXiv:1002.4668 (2010). https://arxiv.org/abs/1002.4668.

Binoy Kurikaparambil Revi. “Multithreading: Threading State Machine.” International Journal for Multidisciplinary Research 1, no. 1 (2019). DOI:10.36948/ijfmr.2019.v01i01.27006.

Hossam, Mahmoud Ahmed, Hala Mousher Ebied, and Mohamed Abdel-Aziz. “Hybrid Cluster of Multicore CPUs and GPUs for Accelerating Hyperspectral Image Hierarchical Segmentation.” In Proceedings of the 8th International Conference on Computer Engineering & Systems (ICCES ’13), 262–267. Cairo: IEEE, December 2013.

IBM Corporation. “Multithreaded Programming.” AIX Version 7.3 Documentation. IBM. Accessed May 11, 2025. https://www.ibm.com/docs/tr/aix/7.3.0?topic=concepts-multithreaded-programming.

Jeziorski, Jakub, Jonathan Penner, and Filament Contributors. “Multithreading USD and Qt: Adding Concurrency to Filament.” In DigiPro ’24: Proceedings of the 2024 Digital Production Conference, July 27 2024. ACM. DOI:10.1145/3665320.3670992.

Plšičík, Robert, and Matúš Danko. “Implementation C++/Qt Framework for CAN Communication.” Transportation Research Procedia 74 (2023): 946–953. https://doi.org/10.1016/j.trpro.2023.01.088.

Qt Company. “Multithreading Technologies in Qt.” Qt 6.9 Documentation. The Qt Company, 2023. https://doc.qt.io/qt-6/threads-technologies.html.

Qt Company. “Producer and Consumer Using Semaphores.” Qt 6.9 Documentation. The Qt Company, 2023. https://doc.qt.io/qt-6/qsemaphore.html#producer-and-consumer-example.

Qt Company. “QObject Class Reference.” Qt 6.9 Documentation. The Qt Company, 2023. https://doc.qt.io/qt-6/qobject.html.

Qt Company. “Reentrancy and Thread-Safety.” Qt 6.9 Documentation. The Qt Company, 2023. https://doc.qt.io/qt-6/threads-reentrancy.html.

Qt Company. “Thread Support in Qt.” Qt 4.4 Documentation (Archived). The Qt Company, 2008. https://doc.qt.io/archives/qt-4.4/threads.html.

Qt Company. “Threading Basics.” Qt 5.15 Documentation. The Qt Company, 2019. https://doc.qt.io/qt-5/thread-basics.html.

Summerfield, Mark.C++ GUI Programming with Qt 4. Upper Saddle River, NJ: Addison-Wesley, 2007.

Tosi, Giovanni. 2021. “Real-Time Processing and Resource Management on Heterogeneous Architectures.” Master’s thesis, Politecnico di Milano. https://www.politesi.polimi.it/retrieve/a81cb05c-fe11-616b-e053-1605fe0a889a/tesi.pdf.

Dipnot

[1]

Aldinucci, Marco, Marco Danelutto, Peter Kilpatrick, Massimiliano Meneghin, and Massimo Torquati. “Accelerating Sequential Programs Using FastFlow and Self-Offloading.” arXiv, arXiv:1002.4668 (2010). https://arxiv.org/abs/1002.4668.

Ayrıca Bakınız

Yazarın Önerileri

Threads ( İş Parçacıkları)Th

Threads ( İş Parçacıkları)

Bilişim Ve İletişim Teknolojileri +1

Sen de Değerlendir!

0 Değerlendirme

Yazar Bilgileri

Avatar
Ana YazarBüşra Önal11 Mayıs 2025 07:53
KÜRE'ye Sor