C ve Win32 ile Çoklu İş Parçacığı Kullanımı

Microsoft C/C++ derleyicisi (MSVC), çok iş parçacıklı uygulamalar oluşturmak için destek sağlar. Uygulamanızın kullanıcı arabiriminin yanıt vermemeye başlamasına neden olacak pahalı işlemler gerçekleştirmesi gerekiyorsa birden fazla iş parçacığı kullanmayı göz önünde bulundurun.

MSVC ile birden çok iş parçacığıyla programlamanın birkaç yolu vardır: C++/WinRT ve Windows Çalışma Zamanı kitaplığını, Microsoft Foundation Sınıfı (MFC) kitaplığını, C++/CLI ve .NET çalışma zamanını ya da C çalışma zamanı kitaplığını ve Win32 API'sini kullanabilirsiniz. Bu makale, C'de çoklu iş parçacığı kullanımı hakkındadır. Örneğin kod, bkz . C'de örnek çoklu iş parçacığı programı.

Çoklu iş parçacığı kullanan programlar

İş parçacığı temelde bir program aracılığıyla yürütme yoludur. Ayrıca Win32'nin zamanlandığı en küçük yürütme birimidir. İş parçacığı bir yığından, CPU yazmaçlarının durumundan ve sistem zamanlayıcısının yürütme listesindeki bir girdiden oluşur. Her iş parçacığı tüm sürecin kaynaklarını paylaşır.

Bir işlem bir veya daha fazla iş parçacığından ve bir programın bellekteki kodundan, verilerinden ve diğer kaynaklarından oluşur. Tipik program kaynakları açık dosyalar, semaforlar ve dinamik olarak ayrılmış bellektir. Sistem zamanlayıcı iş parçacıklarından birini yürütme denetimi verdiğinde bir program yürütülür. Zamanlayıcı, hangi iş parçacıklarının ne zaman çalıştırılması gerektiğini belirler. Yüksek öncelikli iş parçacıkları görevlerini tamamlarken düşük öncelikli iş parçacıklarının beklemesi gerekebilir. Çok işlemcili makinelerde zamanlayıcı, CPU yükünü dengelemek için tek tek iş parçacıklarını farklı işlemcilere taşıyabilir.

Bir işlemdeki her iş parçacığı bağımsız olarak çalışır. Bunları birbirleri için görünür hale getirmediğiniz sürece, iş parçacıkları ayrı ayrı yürütülür ve bir işlemdeki diğer iş parçacıklarının farkında değil. Ancak ortak kaynakları paylaşan iş parçacıklarının semaforları veya başka bir işlemler arası iletişim yöntemini kullanarak çalışmalarını koordine etmesi gerekir. İş parçacıklarını eşitleme hakkında daha fazla bilgi için bkz . Çok İş Parçacıklı Win32 Programı Yazma.

Çoklu iş parçacığı kullanımı için kitaplık desteği

CRT'nin tüm sürümleri artık bazı işlevlerin kilitlenmeyen sürümleri dışında çoklu iş parçacığı kullanımı desteğine sahiptir. Daha fazla bilgi için bkz . Çok iş parçacıklı kitaplıkların performansı. Kodunuzla bağlantı sağlanabilir CRT sürümleri hakkında bilgi için bkz . CRT kitaplık özellikleri.

Çoklu iş parçacığı kullanımı için dosyaları ekleme

Standart CRT ekleme dosyaları, C çalışma zamanı kitaplığı işlevlerini kitaplıklarda uygulandığında bildirir. Derleyici seçenekleriniz __fastcall veya __vectorcall çağırma kurallarını belirtirse, derleyici tüm işlevlerin yazmaç çağırma kuralı kullanılarak çağrılması gerektiğini varsayar. Çalışma zamanı kitaplığı işlevleri C çağırma kuralını kullanır ve standart dosyalardaki bildirimler derleyiciye bu işlevlere doğru dış başvurular oluşturmasını söyler.

İş parçacığı denetimi için CRT işlevleri

Tüm Win32 programlarının en az bir iş parçacığı vardır. Herhangi bir iş parçacığı ek iş parçacıkları oluşturabilir. bir iş parçacığı işini hızlı bir şekilde tamamlayıp sonlandırabilir veya programın ömrü boyunca etkin kalabilir.

CRT kitaplıkları iş parçacığı oluşturma ve sonlandırma için aşağıdaki işlevleri sağlar: _beginthread, _beginthreadex, _endthread ve _endthreadex.

_beginthread ve _beginthreadex işlevleri yeni bir iş parçacığı oluşturur ve işlem başarılı olursa bir iş parçacığı tanımlayıcısı döndürür. İş parçacığı yürütmeyi tamamlarsa otomatik olarak sonlandırılır. Ya da veya çağrısıyla _endthread _endthreadexkendisini sonlandırabilir.

Not

Libcmt.lib ile oluşturulmuş bir programdan C çalışma zamanı yordamlarını çağırırsanız, iş parçacıklarınızı veya _beginthreadex işleviyle _beginthread başlatmanız gerekir. Win32 işlevlerini ExitThread ve CreateThreadkullanmayın. SuspendThread Askıya alınan iş parçacığının C çalışma zamanı veri yapısına erişimini tamamlamasını bekleyen birden fazla iş parçacığı engellendiğinde kullanılması kilitlenmeye neden olabilir.

_beginthread ve _beginthreadex işlevleri

_beginthread ve _beginthreadex işlevleri yeni bir iş parçacığı oluşturur. İş parçacığı, bir işlemin kodunu ve veri kesimlerini işlemdeki diğer iş parçacıklarıyla paylaşır, ancak kendi benzersiz yazmaç değerlerine, yığın alanına ve geçerli yönerge adresine sahiptir. Sistem, bir işlemdeki tüm iş parçacıklarının eşzamanlı olarak yürütülebilmesi için her iş parçacığına CPU süresi verir.

_beginthreadve _beginthreadex Win32 API'sindeki CreateThread işlevine benzer ancak şu farklılıklara sahiptir:

  • Belirli C çalışma zamanı kitaplık değişkenlerini başlatır. Bu yalnızca iş parçacıklarınızda C çalışma zamanı kitaplığını kullanıyorsanız önemlidir.

  • CreateThread güvenlik öznitelikleri üzerinde denetim sağlamaya yardımcı olur. Askıya alınmış durumda bir iş parçacığı başlatmak için bu işlevi kullanabilirsiniz.

_beginthread ve _beginthreadex başarılı olursa yeni iş parçacığına bir tanıtıcı veya hata varsa bir hata kodu döndür.

_endthread ve _endthreadex işlevleri

_endthread işlevi tarafından _beginthread oluşturulan bir iş parçacığını sonlandırır (ve benzer şekilde tarafından _endthreadex oluşturulan _beginthreadexbir iş parçacığını sonlandırır). İş parçacıkları bittiğinde otomatik olarak sonlanır. _endthread ve _endthreadex bir iş parçacığının içinden koşullu sonlandırma için yararlıdır. Örneğin iletişim işlemeye ayrılmış bir iş parçacığı, iletişim bağlantı noktasının denetimini alamıyorsa sonlandırılabilir.

Çoklu iş parçacığı kullanan Win32 programı yazma

Birden çok iş parçacığına sahip bir program yazarken, programdaki kaynakların davranışını ve kullanımını koordine etmeniz gerekir. Ayrıca, her iş parçacığının kendi yığınını aldığından emin olun.

İş parçacıkları arasında ortak kaynakları paylaşma

Her iş parçacığının kendi yığını ve CPU yazmaçlarının kendi kopyası vardır. Dosyalar, statik veriler ve yığın belleği gibi diğer kaynaklar, işlemdeki tüm iş parçacıkları tarafından paylaşılır. Bu ortak kaynakları kullanan iş parçacıkları eşitlenmelidir. Win32 semaforlar, kritik bölümler, olaylar ve mutex'ler dahil olmak üzere kaynakları eşitlemek için çeşitli yollar sağlar.

Birden çok iş parçacığı statik verilere erişirken, programınızın olası kaynak çakışmaları sağlaması gerekir. Bir iş parçacığının, öğelerin başka bir iş parçacığı tarafından görüntülenmesi için x,y koordinatları içeren statik veri yapısını güncelleştirdiği bir program düşünün. Güncelleştirme iş parçacığı x koordinatını değiştirirse ve y koordinatını değiştirmeden önce ön damgalıysa, görüntü iş parçacığı y koordinatı güncelleştirilmeden önce zamanlanabilir. Öğe yanlış konumda görüntülenir. Yapıya erişimi denetlemek için semaforlar kullanarak bu sorundan kaçınabilirsiniz.

Mutex (mutual exclusion kısaltması), birbiriyle zaman uyumsuz olarak yürütülen iş parçacıkları veya işlemler arasında iletişim kurmanın bir yoludur. Bu iletişim, genellikle kaynağı kilitleyip kilidini açarak paylaşılan kaynağa erişimi denetleyerek birden çok iş parçacığının veya işlemin etkinliklerini koordine etmek için kullanılabilir. Bu x,y koordinat güncelleştirme sorununu çözmek için güncelleştirme iş parçacığı, güncelleştirmeyi gerçekleştirmeden önce veri yapısının kullanımda olduğunu belirten bir mutex ayarlar. Her iki koordinat da işlendikten sonra muteks temizlenebilir. Görüntü iş parçacığı, görüntüyü güncelleştirmeden önce mutex'in net olmasını beklemelidir. Bu mutex bekleme işlemi genellikle bir mutex üzerinde engelleme olarak adlandırılır, çünkü işlem engellenir ve mutex temizlenene kadar devam edemez.

Örnek Çoklu İş Parçacığı C Programı'nda gösterilen Bounce.c programı , ekran güncelleştirmelerini koordine etmek için adlı ScreenMutex bir mutex kullanır. Görüntü iş parçacıklarından biri ekrana yazmaya her hazır olduğunda, çağrısının zaman aşımına ScreenMutex değil, mutex üzerinde engellenmesi gerektiğini belirtmek WaitForSingleObject için tutamacı ile ve sabit SONSUZ olarak çağırırWaitForSingleObject. AçıksaScreenMutex, wait işlevi diğer iş parçacıklarının görüntüye müdahale etmemesi için mutex'i ayarlar ve iş parçacığını yürütmeye devam eder. Aksi takdirde, iş parçacığı mutex temizlenene kadar engeller. İş parçacığı görüntü güncelleştirmesini tamamladığında çağırarak ReleaseMutexmutex'i serbest bırakır.

Ekran görüntüleri ve statik veriler, dikkatli bir yönetim gerektiren kaynaklardan yalnızca ikisidir. Örneğin, programınızın aynı dosyaya erişen birden çok iş parçacığı olabilir. Başka bir iş parçacığı dosya işaretçisini taşımış olabileceğinden, okumadan veya yazmadan önce her iş parçacığının dosya işaretçisini sıfırlaması gerekir. Buna ek olarak, her iş parçacığı işaretçiyi konumlandıran ile dosyaya eriştiği zaman arasında önceden işaretlenmediğinden emin olmalıdır. Bu iş parçacıkları, ve çağrılarıyla her bir dosya erişimini köşeli ayraçla ayırarak dosyaya erişimi WaitForSingleObject ReleaseMutex koordine etmek için bir semafor kullanmalıdır. Aşağıdaki kod örneği bu tekniği gösterir:

HANDLE    hIOMutex = CreateMutex (NULL, FALSE, NULL);

WaitForSingleObject( hIOMutex, INFINITE );
fseek( fp, desired_position, 0L );
fwrite( data, sizeof( data ), 1, fp );
ReleaseMutex( hIOMutex);

İş parçacığı yığınları

Bir uygulamanın varsayılan yığın alanının tümü, iş parçacığı 1 olarak bilinen ilk yürütme iş parçacığına ayrılır. Sonuç olarak, programınızın ihtiyaç duyduğu her ek iş parçacığı için ayrı bir yığın için ne kadar bellek ayrılacağını belirtmeniz gerekir. İşletim sistemi, gerekirse iş parçacığı için ek yığın alanı ayırır, ancak varsayılan bir değer belirtmeniz gerekir.

Çağrıdaki _beginthread ilk bağımsız değişken, iş parçacıklarını yürüten işlevin işaretçisidir BounceProc . İkinci bağımsız değişken, iş parçacığı için varsayılan yığın boyutunu belirtir. Son bağımsız değişken, öğesine BounceProcgeçirilen bir kimlik numarasıdır. BounceProc , rastgele sayı oluşturucuyu görmek ve iş parçacığının renk özniteliğini ve görüntüleme karakterini seçmek için kimlik numarasını kullanır.

C çalışma zamanı kitaplığına veya Win32 API'sine çağrıda bulunan iş parçacıkları, çağırdıkları kitaplık ve API işlevleri için yeterli yığın alanına izin vermelidir. C printf işlevi 500 bayttan fazla yığın alanı gerektirir ve Win32 API yordamlarını çağırırken 2K bayt yığın alanınız olmalıdır.

Her iş parçacığının kendi yığını olduğundan, mümkün olduğunca az statik veri kullanarak veri öğeleri üzerinde olası çakışmaları önleyebilirsiniz. Programınızı, iş parçacığına özel olabilecek tüm veriler için otomatik yığın değişkenlerini kullanacak şekilde tasarlayabilirsiniz. Bounce.c programındaki tek genel değişkenler, başlatıldıktan sonra hiçbir zaman değişmemiş olan mutex'ler veya değişkenlerdir.

Win32 ayrıca iş parçacığı başına verileri depolamak için İş parçacığı yerel depolama (TLS) sağlar. Daha fazla bilgi için bkz . İş parçacığı yerel depolama (TLS).

Çoklu iş parçacığı kullanan programlarda sorun alanlarından kaçınma

Çok iş parçacıklı bir C programı oluştururken, bağlarken veya yürütürken karşılaşabileceğiniz birkaç sorun vardır. Daha yaygın sorunlardan bazıları aşağıdaki tabloda açıklanmıştır. (MFC açısından benzer bir tartışma için bkz. Çoklu İş Parçacığı Kullanımı: Programlama İpuçları.)

Sorun Olası neden
Programınızın koruma ihlaline neden olduğunu gösteren bir ileti kutusu alırsınız. Birçok Win32 programlama hatası koruma ihlallerine neden olur. Koruma ihlallerinin yaygın nedenlerinden biri, verilerin null işaretçilere dolaylı olarak atanmasidir. Programınızın buna ait olmayan belleğe erişmeye çalışmasıyla sonuçlandığından, bir koruma ihlali verilir.

Koruma ihlalinin nedenini algılamanın kolay bir yolu, programınızı hata ayıklama bilgileriyle derlemek ve ardından Visual Studio ortamında hata ayıklayıcı aracılığıyla çalıştırmaktır. Koruma hatası oluştuğunda, Windows denetimi hata ayıklayıcıya aktarır ve imleç soruna neden olan satıra konumlandırılır.
Programınız çok sayıda derleme ve bağlantı hatası oluşturur. Derleyicinin uyarı düzeyini en yüksek değerlerinden birine ayarlayarak ve uyarı iletilerini dikkate alarak birçok olası sorunu ortadan kaldırabilirsiniz. Düzey 3 veya düzey 4 uyarı düzeyi seçeneklerini kullanarak, istenmeyen veri dönüştürmelerini, eksik işlev prototiplerini ve ANSI dışı özelliklerin kullanımını algılayabilirsiniz.

Ayrıca bkz.

Eski Kod için Çoklu İş Parçacığı Kullanma Desteği (Visual C++)
C'de örnek çoklu iş parçacığı programı
İş parçacığı yerel depolama (TLS)
C++/WinRT ile eşzamanlılık ve zaman uyumsuz işlemler
C++ ve MCF ile Çoklu İş Parçacığı Kullanımı