Çok iş parçacıklı bir motordaki iş parçacıkları arasındaki iletiyi daha az hantal hale nasıl getirebilirim?


18

Şu anda üzerinde çalıştığım C ++ motoru birkaç büyük iş parçacığına ayrıldı - Nesil (yordamsal içeriğimi oluşturmak için), Oynanış (AI, komut dosyaları, simülasyon için), Fizik ve İşleme.

İş parçacıkları, iş parçacığından iş parçacığına geçen küçük ileti nesneleri aracılığıyla birbirleriyle iletişim kurar. Adımlamadan önce, bir iş parçacığı tüm gelen iletilerini işler - dönüşüm, nesne ekleme ve kaldırma vb.

Sürecin başlarında ve birkaç şey fark ettim:

  1. Mesajlaşma sistemi hantaldır. Yeni bir ileti türü oluşturmak, temel Message sınıfının alt sınıfının sınıflandırılması, türü için yeni bir numaralandırma oluşturulması ve iş parçacıklarının yeni ileti türünü nasıl yorumlaması gerektiğine ilişkin mantık yazmak anlamına gelir. Bu geliştirme için bir hız tümseği ve yazım hatası tarzı eğilimli. (Sidenote- bunun üzerinde çalışmak bana dinamik dillerin ne kadar harika olabileceğini takdir etmemi sağlıyor!)

    Bunu yapmanın daha iyi bir yolu var mı? Bunu otomatik yapmak için boost :: bind gibi bir şey kullanmalı mıyım? Endişeliysem, bunu söyleyebilme yeteneğimi kaybedersem, mesajları türe veya başka bir şeye göre sıralayabilirim. Bu tür bir yönetimin gerekli olup olmayacağından emin değilim.

  2. İlk nokta önemlidir, çünkü bu iplikler çok fazla iletişim kurar. Mesaj oluşturma ve iletme, olayları gerçekleştirmenin büyük bir parçasıdır. Bu sistemi kolaylaştırmak istiyorum, aynı zamanda yardımcı olabilecek diğer paradigma da açık olmak istiyorum. Bunu kolaylaştırmak için düşünmem gereken farklı çok iş parçacıklı tasarımlar var mı?

    Örneğin, nadiren yazılan, ancak birden çok iş parçacığından sıkça okunan bazı kaynaklar vardır. Muteksler tarafından korunan ve tüm iş parçacıklarının erişebileceği paylaşılan veriye sahip olma fikrine açık olmalı mıyım?

Bu benim ilk defa çoklu iş parçacıkları akıldan başlayarak bir şeyler tasarlıyor. Bu erken aşamada aslında çok iyi gittiğini düşünüyorum (düşünerek) ama ölçeklendirme konusunda endişeliyim ve yeni şeyler uygulamadaki kendi verimliliğim.


6
Burada gerçekten tek ve yönlendirilmiş bir soru yok ve bu nedenle bu yazı bu sitenin Soru-Cevap stili için uygun değil. Gönderinizi soru başına bir tane olmak üzere farklı gönderilere ayırmanızı ve soruları yeniden odaklamanızı öneririm, böylece belirsiz bir ipucu veya öneri yerine gerçekte yaşadığınız belirli bir soruyu sorarlar.

2
Daha genel bir sohbete katılmak istiyorsanız, bu yayını gamedev.net'teki forumlarda denemenizi tavsiye ederim . Josh'un dediği gibi, "sorunuz" tek bir soru olmadığından, StackExchange biçiminde konaklamak oldukça zor olacaktır.
Cypher

Geri bildiriminiz için teşekkürler çocuklar! Daha fazla bilgiye sahip birisinin, sorunlarımın birkaçını aynı anda çözebilecek tek bir kaynak / deneyim / paradigma olabileceğini umuyordum. Büyük bir fikrin çeşitli problemlerimi eksik olduğum bir şeye sentezleyebileceği hissine kapılıyorum ve benden daha fazla deneyime sahip birisinin bunu tanıyabileceğini düşünüyordum ... Ama belki değil ve her nasılsa alınan puanlar !
Raptormeat

Başlığınızı mesaj geçişine daha spesifik olacak şekilde yeniden adlandırdım, çünkü "ipuçları" türü sorular, çözülmesi gereken belirli bir sorun olmadığını ima eder (bu nedenle bu günlerde "gerçek bir soru" olarak kapanmam).
Tetrad

Fizik ve oynanış için ayrı konulara ihtiyacınız olduğundan emin misiniz? Bu ikisi çok iç içe geçmiş gibi görünüyor. Ayrıca, her birinin nasıl ve kiminle nasıl iletişim kurduğunu bilmeden nasıl öneride bulunacağınızı bilmek zordur.
Nicol Bolas

Yanıtlar:


10

Daha geniş sorununuz için, zincirler arası iletişimi mümkün olduğunca azaltmanın yollarını bulmaya çalışın. Yapabiliyorsanız, senkronizasyon sorunlarından tamamen kaçınmak daha iyidir. Bu, verilerinizi iki kez arabelleğe alarak, tek bir güncelleme gecikmesi sunarak ancak paylaşılan verilerle çalışma görevini büyük ölçüde kolaylaştırarak gerçekleştirilebilir.

Bir yana, alt sistem tarafından iş parçacığı değil, iş parçacığı çatal yerine iplik yumurtlama veya iplik havuzları kullanmayı düşündünüz mü? (bkz bu parçacığı havuzu için, size sorununuzla açısından.) Bu kısa kağıt kısaca havuz desenin amacını ve kullanımını açıklar. Bu bilgilendirici cevaplara bakınAyrıca. Burada belirtildiği gibi, iş parçacığı havuzları ölçeklenebilirliği bir bonus olarak artırır. Ve her seferinde yeni bir oyun veya motor yazdığınızda güzel oynamak için alt sistem tabanlı iş parçacığı elde etmek yerine "bir kez yazın, her yerde kullanın" dır. Orada da çok sayıda sağlam üçüncü taraf iplik havuzu çözümü var. İplik yumurtlama ile başlamak ve iplikleri yumurtlama ve yok etme yükünün hafifletilmesi gerekiyorsa, daha sonra iplik havuzları için yolunuzu çalıştırmak daha kolay olacaktır.


1
Kontrol etmek için belirli iplik havuzu kütüphaneleri için herhangi bir öneriniz var mı?
imre

Nick- Yanıt için çok teşekkürler. İlk noktanıza gelince, bence bu harika bir fikir ve muhtemelen hareket edeceğim yön. Şu anda neyin çift tamponlu olması gerektiğini bilmediğim kadar erken. Bunu akılda tutacağım çünkü zamanla katılaşıyor. İkinci noktanıza - öneri için teşekkürler! Evet, iş parçacığı görevlerinin faydaları açıktır. Bağlantılarınızı okuyacağım ve düşüneceğim. Benim için işe yarayıp yaramayacağını / benim için nasıl işe yarayacağını% 100 emin değilim ama kesinlikle ciddi bir düşünce vereceğim. Teşekkürler!
Raptormeat

1
@imre Boost kütüphanesine bakın - gelecekleri var, bu şeylere yaklaşmanın güzel / kolay bir yolu.
Jonathan Dickinson

5

Farklı çok iş parçacıklı tasarımlar hakkında sorular sordunuz. Bir arkadaşım bana bu yöntemin oldukça havalı olduğunu düşündüğümü söyledi.

Fikir, her oyun varlığının 2 kopyası olacağıdır (savurgan, biliyorum). Bir kopya mevcut kopya, diğeri önceki kopya olacaktır. Mevcut kopya kesinlikle yazılır ve geçmiş kopya kesinlikle okunur . Güncellemeye gittiğinizde, varlık listenizin aralıklarını uygun gördüğünüz kadar çok iş parçacığına atarsınız. Her iş parçacığının, atanan aralıktaki mevcut kopyalara yazma erişimi vardır ve her iş parçacığının varlıkların tüm geçmiş kopyalarına okuma erişimi vardır ve böylece atanmış olan mevcut kopyaları geçmiş kopyalardaki verileri kullanarak kilitlemesiz güncelleyebilir. Her kare arasında, mevcut kopya geçmiş kopya olur, ancak rollerin değişimini yapmak istersiniz.


4

Sadece C # ile aynı sorunu yaşadık. Yeni mesaj oluşturma kolaylığı (veya eksikliği) hakkında uzun ve zor düşündükten sonra, yapabileceğimiz en iyi şey onlar için bir kod üreteci yapmaktı. Biraz çirkin, ama kullanılabilir: sadece mesaj içeriğinin bir açıklaması verildiğinde, mesaj sınıfı, numaralandırmalar, yer tutucu işleme kodu vb. Üretir - tüm bu kod her seferinde hemen hemen aynıdır ve gerçekten yazım eğilimli.

Bundan tamamen memnun değilim, ama tüm bu kodu elle yazmaktan daha iyi.

Paylaşılan veriler ile ilgili olarak, en iyi cevap elbette "duruma bağlıdır". Ancak genellikle, bazı veriler sık ​​sık okunursa ve birçok iş parçacığı tarafından ihtiyaç duyulursa, paylaşılması buna değer. İplik güvenliği için, en iyi bahsiniz onu değişmez kılmaktır , ancak bu söz konusu değilse, mutex yapabilir. C # 'da ReaderWriterLockSlim, özellikle bu gibi durumlar için tasarlanmış bir sınıf vardır ; Eminim bir C ++ eşdeğeri vardır.

Büyük olasılıkla ilk sorununuzu çözen iş parçacığı iletişimi için başka bir fikir , iletiler yerine işleyicileri iletmektir . C ++ bu nasıl çalışacağından emin değilim, ama C # delegatebaşka bir iş parçacığına bir nesne gönderebilirsiniz (gibi, bir tür ileti kuyruğuna ekleyin) ve aslında bu iş parçacığı alıcı iş parçacığından çağırabilirsiniz . Bu, yerinde "geçici" mesajlar oluşturmayı mümkün kılar. Sadece bu fikirle oynadım, aslında üretimde hiç denemedim, bu yüzden aslında kötü olabilir.


Tüm harika bilgiler için teşekkürler! İşleyicilerle ilgili son bit, işlevleri geçmek için bağlama veya functors kullanma hakkında bahsettiğim şeye benziyor. Ben fikir gibi-denemek ve berbat ya da harika olup olmadığını görmek olabilir: D Sadece bir CallDelegateMessage sınıfı oluşturmak ve ayak parmağımı suya daldırma ile başlayabilir.
Raptormeat

1

Sadece bazı dişli oyun kodlarının tasarım aşamasındayım, bu yüzden gerçek fikirlerimi değil sadece düşüncelerimi paylaşabilirim. Bununla birlikte, aşağıdaki satırlar boyunca düşünüyorum:

  • Oyun verilerinin çoğu salt okunur erişim için paylaşılmalıdır .
  • Bir tür mesajlaşma kullanarak veri yazmak mümkündür.
  • Başka bir evre okurken verileri güncellemekten kaçınmak için, oyun döngüsünün iki farklı aşaması vardır: okuma ve güncelleme.
  • Okuma aşamasında:
  • Paylaşılan tüm veriler, tüm evreler için salt okunurdur.
  • İş parçacıkları, (iş parçacığı-yerel depolamayı kullanarak) bir şeyler hesaplayabilir ve daha sonra uygulanacak temelde komut / ileti nesneleri olan güncelleme istekleri üretebilir .
  • Güncelleme aşamasında:
  • Paylaşılan tüm veriler salt yazılır. Veriler bilinmeyen / kararsız bir durumda varsayılmalıdır.
  • Bu, güncelleme isteği nesnelerinin işlendiği yerdir.

Teoride bu (emin olmasa da) bu hem okuma hem de güncelleme aşamalarında, herhangi bir sayıda iş parçacığının aynı anda minimum senkronizasyonla çalışabileceği anlamına gelmesi gerektiğini düşünüyorum. Okuma aşamasında hiç kimse paylaşılan verileri yazmaz, bu nedenle eşzamanlılık sorunları ortaya çıkmamalıdır. Güncelleme aşaması, bu daha karmaşık. Aynı veri parçasındaki paralel güncellemeler bir sorun olabilir, bu nedenle burada bazı senkronizasyonlar yapılır. Ancak, farklı veri kümelerinde çalıştıkları sürece, gelişigüzel sayıda güncelleme iş parçacığı çalıştırabilirim.

Toplamda, bu yaklaşımın bir iş parçacığı havuzu sistemine iyi borç vereceğini düşünüyorum. Sorunlu kısımlar:

  • Güncelleme iş parçacıklarını senkronize etme (birden fazla iş parçacığının aynı veri kümesini güncellemeye çalışmadığından emin olun).
  • Okuma aşamasında hiçbir iş parçacığının yanlışlıkla paylaşılan verileri yazamamasını sağlamak. Korkarım programlama hataları için çok fazla alan olacaktır ve bunların ne kadarının hata ayıklama araçları tarafından kolayca yakalanabileceğinden emin değilim.
  • Hemen okumak için ara sonuçlarınıza güvenemeyeceğiniz şekilde kod yazma. Yani, x += 2; if (x > 5) ...x paylaşılıyorsa yazamazsınız . X'in yerel bir kopyasını oluşturmanız veya bir güncelleme isteği oluşturmanız ve yalnızca bir sonraki çalıştırmada koşullu yapmanız gerekir. İkincisi, çok sayıda ek yerel-yerel durumu koruyan kazan plakası kodunu ifade eder.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.