Okuyuculu böcekler tarafından rahatsız


26

Yönettiğim yeni ekibimde kodumuzun çoğunluğu platform, TCP soketi ve http ağ kodu. Hepsi C ++. Bunların çoğu takımı terk eden diğer geliştiricilere dayanıyor. Takımdaki mevcut geliştiriciler çok zeki, ancak çoğunlukla deneyim açısından küçük.

En büyük sorunumuz: Çok iş parçacıklı eşzamanlılık hataları. Sınıf kütüphanelerimizin çoğu bazı thread pool sınıfları kullanılarak asenkronize olarak yazılmıştır. Sınıf kitaplıkları üzerindeki yöntemler, genellikle bir havuzdaki iş parçacığı havuzunda uzun süreli alımları sıkar ve daha sonra bu sınıfın geri çağırma yöntemleri farklı bir iş parçacığına çağrılır. Sonuç olarak, yanlış diş açma varsayımlarını içeren çok sayıda ileri düzeyde hatamız var. Bu, eşzamanlılık sorunlarına karşı korunmak için yalnızca kritik bölümlerin ve kilitlerin ötesine geçen ince hatalara neden olur.

Bu problemleri daha da zorlaştıran şey, tamir etme girişimlerinin çoğu zaman yanlış olmasıdır. Bazı denemeler yapan takımın (veya eski kodun kendisinde) gözlemlediğim bazı hatalar aşağıdaki gibi bir şey içeriyor:

Yaygın hata # 1 - Eşzamanlılık sorununu yalnızca paylaşılan verilerin çevresine kilitleyerek düzeltmek, ancak yöntemler beklenen bir sırada çağrılmadığında ne olacağını unutmak. İşte çok basit bir örnek:

void Foo::OnHttpRequestComplete(statuscode status)
{
    m_pBar->DoSomethingImportant(status);
}

void Foo::Shutdown()
{
    m_pBar->Cleanup();
    delete m_pBar;
    m_pBar=nullptr;
}

Şimdi, OnHttpNetworkRequestComplete gerçekleşirken Shutdown'ın çağrılabileceği bir hatamız var. Bir test cihazı hatayı bulur, kilitlenme çöplüğünü yakalar ve hatayı bir geliştiriciye atar. O da böceği böceği böyle düzeltir.

void Foo::OnHttpRequestComplete(statuscode status)
{
    AutoLock lock(m_cs);
    m_pBar->DoSomethingImportant(status);
}

void Foo::Shutdown()
{
    AutoLock lock(m_cs);
    m_pBar->Cleanup();
    delete m_pBar;
    m_pBar=nullptr;
}

Yukarıdaki düzeltme daha da ince bir dava olduğunu anlayana kadar iyi görünüyor. OnHttpRequestComplete geri çağrılmadan önce Kapatma çağrılırsa ne olur ? Ekibimin sahip olduğu gerçek dünya örnekleri daha da karmaşık ve kod incelemesi sırasında son durumları tespit etmek daha da zor.

Yaygın Hata # 2 - kilitlenmeden kilitlenmeden çıkma kilitlenme sorunlarını gidermek, diğer iş parçacığının bitmesini bekleyin, sonra kilidi tekrar girin - ancak nesnenin diğer iş parçacığı tarafından henüz güncellendiği durumuyla ilgilenmeden!

Genel Hata # 3 - Nesneler referans sayılsa bile, kapatma dizisi göstericiyi "serbest bırakır". Ancak, örneğini serbest bırakmak için hala çalışan iş parçacığını beklemeyi unutur. Bu nedenle, bileşenler temiz bir şekilde kapatılır, ardından daha fazla arama beklemeyen bir nesnede sahte veya geç geri aramalar başlatılır.

Başka kenar davaları da var, ama sonuç şu:

Çok iş parçacıklı programlama, akıllı insanlar için bile oldukça kolaydır.

Bu hataları yakaladığımda, her geliştiriciyle daha uygun bir düzeltme geliştirmek için hataları tartışarak zaman harcıyorum. Ancak, “doğru” düzeltmenin dokunmayı içereceği muazzam miktarda eski kod nedeniyle her konunun nasıl çözüleceği konusunda sık sık karıştığından şüpheleniyorum.

Yakında satışa çıkacağız ve eminim uyguladığımız yamalar gelecek sürüm için geçerli olacak. Daha sonra, gerektiğinde kod tabanını ve yeniden düzenleyiciyi geliştirmek için biraz zamanımız olacak. Her şeyi yeniden yazmak için zamanımız olmayacak. Ve kodun çoğunluğu o kadar da kötü değil. Ancak, diş açma sorunlarından tamamen kaçınılabilecek şekilde yeniden kodlama kodunu arıyorum.

Düşündüğüm tek yaklaşım bu. Her önemli platform özelliği için, tüm olayların ve ağ geri aramalarının birleştirildiği tek bir iş parçacığına sahip olun. Bir mesaj döngüsünün kullanımıyla Windows'taki COM dairesine benzer. Uzun engelleme işlemleri hala bir iş havuzu iş parçacığına gönderilebilir, ancak tamamlama geri çağrısı bileşen iş parçacığında çağrılır. Bileşenler muhtemelen aynı dişi bile paylaşabilirdi. Daha sonra ipliğin içinde çalışan tüm sınıf kütüphaneleri, tek bir dişli dünya varsayımı altında yazılabilir.

Bu yolda ilerlemeden önce, çok iş parçacıklı konularla ilgilenmek için başka standart teknikler veya tasarım desenleri olup olmadığını da çok ilgileniyorum. Ve vurgulamalıyım - muteksilerin ve semaforların temellerini tanımlayan kitabın ötesinde bir şey. Ne düşünüyorsun?

Ayrıca bir yeniden düzenleme işlemine yönelik diğer yaklaşımlarla da ilgileniyorum. Aşağıdakilerden herhangi birini içerenler:

  1. Konu etrafındaki tasarım desenleri üzerine literatür veya yazılar. Mutekslere ve semaforlara girişin ötesinde bir şey. Eşzamanlı paralelliğe de ihtiyacımız yok, sadece diğer konuların eşzamansız olaylarını doğru bir şekilde ele almak için bir nesne modeli tasarlamanın yolları .

  2. Çeşitli bileşenlerin iş parçacığını şemalandırmanın yolları; böylece çözüm ve çözümlerini geliştirmek kolay olacaktır. (Yani, nesneler ve sınıflar arasında iş parçacıklarını tartışmak için bir UML'ye eşdeğerdir)

  3. Geliştirme ekibinizi çok iş parçacıklı kod içeren konular hakkında eğitin.

  4. Sen ne yapardın?


23
Bazı insanlar bir sorunla karşılaştığında çoklu iş parçacığı kullanacağımı düşünüyor. Şimdi twolm probları var
Tom Squires

20
Benim için iyi olan şey mümkün olduğunca değişkenlikten kurtulmak . Ben gördüğünüzde değişken nesne yeni bir değer geçmek için durumunun değişmesi, bunun yeni bir geçirerek içine gözden geçirmeniz deneyin değişmez yerine değiştirilen değer tutan bir nesne. Nesne başlatma güvenli bir şekilde yapılırsa, veri yarışlarının yokluğunu garanti eder - oldukça rahatlama
gnat

3
Çok iplikli cehenneme hoş geldiniz. > 20 yıldan beri Ada, Occam, C ++ 'ta çoklu-iplik / paralell programları yazıyorum. Asla kolay değil, her şey çok dikkatli bir düşünce gerektiriyor ve "bunun kolay X yapması kolay" diyen herkes gerçekten neler olduğunu anlamayan bir aptal. İyi şanslar.
Çabuk_sayısı

2
Eşzamanlılığı iyi görmek istiyorsanız Erlang! Gerçekte, istediğiniz şey, garip köşe davalarının ortadan kaldırılacağı, paylaşılan hiçbir şey olmayan aktör modelinin bir şeklidir.
Zachary K

3
@DeadMG Paylaşılan devlet eşzamanlılığının doğal olarak garip köşe davalarına eğilimli olduğunu ve kaçınılması gerektiğini iddia ediyorum. Ama Hey Erlang hakkında bir kitap yazdım
Zachary K

Yanıtlar:


27

Kodunuzda bunun dışında önemli başka sorunlar var. İşaretçiyi elle silmek? Bir cleanupişlev mi arıyorsunuz? Owch. Ayrıca, soru yorumunda doğru şekilde belirtildiği gibi, kilidiniz için RAII kullanmazsınız, bu da epik bir epik başarısızlıktır ve DoSomethingImportantbir istisna atarken , korkunç şeylerin olacağını garanti eder .

Bu çok iş parçacıklı hatanın meydana gelmesi gerçeği, sadece temel sorunun bir belirtisidir - kodunuz herhangi bir iş parçacığı durumunda son derece kötü bir anlam ifade ediyor ve tamamen güvenilmez araçlar ve eski deyimler kullanıyorsunuz. Yerinde olsam, tek bir iplikle işleyişine hayret ederdim, daha da fazlasını.

Genel Hata # 3 - Nesneler referans sayılsa bile, kapatma dizisi göstericiyi "serbest bırakır". Ancak, örneğini serbest bırakmak için hala çalışan iş parçacığını beklemeyi unutur. Bu nedenle, bileşenler temiz bir şekilde kapatılır, ardından daha fazla arama beklemeyen bir nesnede sahte veya geç geri aramalar başlatılır.

Referans saymanın bütün noktası , iş parçacığının örneğini zaten serbest bırakmış olmasıdır . Çünkü eğer değilse, o zaman imha edilemez çünkü ipliğin hala bir referansı vardır.

Kullanın std::shared_ptr. Tüm dişler serbest bırakıldığında (ve dolayısıyla hiç kimse , bu nedenle işaretçisi olmadığı için işlevi çağıramaz), o zaman yıkıcı denir. Bu güvenli garantilidir.

İkinci olarak, Intel'in İplik Yapı Taşları veya Microsoft'un Paralel Desenleri Kütüphanesi gibi gerçek bir iş parçacığı kütüphanesi kullanın. Kendinizinkini yazmak zaman alıcı ve güvenilmezdir ve kodunuz gerek duymadığı iplik ayrıntıları ile doludur. Kendi kilitlerinizi yapmak, kendi bellek yönetiminizi yapmak kadar kötü. Kullanımınız için doğru çalışan birçok genel amaçlı çok kullanışlı iş parçacığı deyimlerini zaten uyguladılar.


Bu iyi bir cevap, fakat aradığım yön değil, çünkü sadece basitlik için yazılmış bir kod örneğini değerlendirmek için çok zaman harcıyor (ürünümüzdeki gerçek kodumuzu yansıtmıyor). Ancak yaptığınız bir yorum hakkında merak ediyorum - "güvenilmez araçlar". Güvenilmez bir araç nedir? Hangi araçları önerirsiniz?
koncurrency

5
@koncurrency: Güvenilmez bir araç, manuel bellek yönetimi veya kendi senkronizasyonunuzu yazma gibi bir şeydir; teoride X sorununu çözer ancak gerçekte o kadar kötüdür ki dev hataları ve problemi çözmenin tek yolunu garanti edersiniz makul bir ölçekte, şu anda sahip olduğunuz şey olan geliştirici zamanının büyük ve orantısız yatırımıdır.
DeadMG

9

Diğer posterler, temel sorunları çözmek için neler yapılması gerektiği konusunda iyi yorumlarda bulundu. Bu gönderi, eski kodun her şeyi doğru şekilde yeniden yapmak için zaman kazanmanız için yeterince iyi bir şekilde eklenmesiyle ilgilidir. Başka bir deyişle, bu işleri yapmanın doğru yolu değil, şimdilik şimdilik aksatmanın bir yolu.

Önemli olayları birleştirme fikriniz iyi bir başlangıçtır. Sipariş bağımlılığının olduğu her yerde tüm anahtar senkronizasyon olaylarını işlemek için tek bir gönderme iş parçacığı kullanacak kadar ileri giderdim. İş parçacığı güvenli bir mesaj sırası oluşturun ve eşzamanlılık açısından hassas işlemleri gerçekleştirdiğiniz her yerde (ayırmalar, temizleme işlemleri, geri aramalar vb.) Yapın, bunun yerine o iş parçacığına bir mesaj gönderin ve işlemi gerçekleştirmesini veya başlatmasını isteyin. Fikir, bu iş parçacığının tüm iş birimi başlatma, durdurma, tahsisat ve temizleme işlemlerini kontrol etmesidir.

Sevk parçacığı yok değil sadece bir yerde birleştirir, açıklanan sorunları çözmek. Beklenmeyen bir sırada gerçekleşen olaylar / mesajlar için hala endişelenmeniz gerekiyor. Önemli çalışma zamanları olan olayların diğer başlıklara gönderilmesi gerekir, bu nedenle paylaşılan verilerde eşzamanlılık sorunu vardır. Bunu hafifletmenin bir yolu, referans olarak veri iletmekten kaçınmaktır. Mümkün olduğunda, gönderme mesajlarındaki veriler, alıcıya ait olacak kopyalar olmalıdır. (Bu, diğerlerinin bahsettiği verileri değiştirilemez kılma çizgileri boyuncadır.)

Bu gönderim yaklaşımının avantajı, gönderim ipliği içinde, belirli işlemlerin sırayla gerçekleştiğini bildiğiniz bir tür güvenli bölgeye sahip olmanızdır. Dezavantajı, bir tıkanıklık ve ek CPU ek yükü yaratmasıdır. İlk önce bunlardan herhangi biri için endişelenmemenizi öneriyorum: Gönderim iş parçacığına mümkün olduğunca ileri giderek bir miktar doğru işlem ölçmeye odaklanın. Ardından, en fazla CPU süresini alanın ne olduğunu görmek için biraz profil oluşturma yapın ve doğru okuyuculu tekniklerle doğru iş parçacığı dışına kaydırmaya başlayın.

Yine, tanımladığım şey işleri yapmanın doğru yolu değil, ancak ticari süreleri karşılayacak kadar küçük artışlarla sizi doğru yola çıkarabilen bir işlemdir.


Mevcut zorlukların üstesinden gelmek için makul bir ara öneri için +1

Evet, araştırdığım yaklaşım bu. Performans hakkında iyi puanlar yükseltin.
koncurrency

Tek bir gönderi ipinden geçmek için işleri değiştirmek hızlı bir yama gibi değil, bana çok büyük bir refaktör gibi geliyor.
Sebastian Redl,

8

Gösterilen koda göre, bir yığın WTF'niz var. Kötü yazılmış bir çok iş parçacıklı uygulamanın artımlı olarak düzeltilmesi imkansız değilse de son derece zordur. Sahiplere, önemli bir yeniden işleme tabi tutulmadan uygulamanın asla güvenilir olmayacağını söyleyin. Onlara, paylaşılan nesnelerle etkileşime giren kodun her bir parçasını inceleme ve yeniden işleme dayalı bir tahmin verin. İlk önce onlara denetim için bir tahmin verin. Ardından, yeniden işleme için bir tahmin verebilirsiniz.

Kodu yeniden işlediğinizde, kodu doğru bir şekilde doğru şekilde yazmayı planlamalısınız. Bunu nasıl yapacağınızı bilmiyorsanız, yapan birini bulun, yoksa aynı yerde kalacaksınız.


Bunu şimdi okudum, cevabım alındıktan sonra. Sadece tanıtım cümlesini sevdiğimi söylemek istedim :)
back2dos

7

Başvurunuzu yeniden düzenlemeye adamaya vaktiniz varsa, oyuncu modeline bir göz atmanızı tavsiye ederim (bakınız örn. Theron , Casablanca , libcppa , C ++ uygulamaları için CAF ).

Aktörler eşzamanlı olarak çalışan ve yalnızca eşzamansız ileti alış verişini kullanarak birbirleriyle iletişim kuran nesnelerdir. Bu nedenle, iş parçacığı yönetimi, muteksler, kilitlenmeler, vb. Tüm problemleri bir aktör uygulama kütüphanesi tarafından ele alınmaktadır ve nesnenin (aktörler) davranışını tekrarlamak için kaynayan uygulama üzerine yoğunlaşabilirsiniz.

  1. Mesajı al
  2. Hesaplama yapmak
  3. Mesaj gönder / diğer oyuncuları yarat / öldür.

Sizin için bir yaklaşım , önce konuyla ilgili bazı okumalar yapmak ve muhtemelen aktör modelinin kodunuza entegre edilip edilemeyeceğini görmek için bir veya iki kütüphaneye bir göz atmak olabilir.

Bu modeli birkaç aydır benim bir projemde kullanıyorum (basitleştirilmiş bir versiyonunu) ve ne kadar sağlam olduğuna şaşırdım.


1
Scala Akka kütüphanesi, çocuklar öldüğünde ebeveynlerin nasıl öldürüleceği hakkında çok şey düşünen güzel bir uygulama. C ++ olmadığını biliyorum, ama bir göz atmaya değer: akka.io
GlenPeterson 12:12

1
@GlenPeterson: Teşekkürler, şu anda en ilginç çözümü düşündüğüm ve hem Java hem de Scala ile çalıştığı akka'yı biliyorum; ancak soru özellikle C ++ 'a yöneliyor. Aksi halde bir etkinlik Erlang'ı düşünebilir. Sanırım Erlang'da çok iş parçacıklı programlamanın tüm baş ağrıları iyi gitti. Ama belki akka gibi çerçeveler çok yaklaşıyor.
Giorgio

“Erlang'da, çok iş parçacıklı programlamanın tüm baş ağrıları iyi gitti.” Bence bu biraz abartılmış olabilir. Veya doğruysa, o zaman performans eksik olabilir. Akka'nın C ++ ile çalışmadığını biliyorum, sadece birden fazla iş parçacığını yönetmek için en son teknolojiye benzediğini söylüyor. Bununla birlikte, iplik güvenli değildir. Oyuncular arasında değişken durumdan geçebilir ve kendinizi ayağınızdan çekebilirsiniz.
GlenPeterson

Ben bir Erlang uzmanı değilim ama AFAIK, her oyuncu ayrı ayrı yönetiliyor ve değişmez mesajlar değiş tokuş ediliyor. Yani gerçekten iş parçacığı ve paylaşılan değişken durumu ile uğraşmak zorunda değilsiniz. Performans muhtemelen C ++ 'dan düşüktür, ancak bu daima soyutlama seviyesini yükselttiğinizde gerçekleşir (yürütme süresini artırır ancak geliştirme süresini azaltır).
Giorgio

Redüktör yorum yazıp bu cevabı nasıl geliştirebileceğimi önerebilir mi?
Giorgio,

6

Yaygın hata # 1 - Eşzamanlılık sorununu yalnızca paylaşılan verilerin çevresine kilitleyerek düzeltmek, ancak yöntemler beklenen bir sırada çağrılmadığında ne olacağını unutmak. İşte çok basit bir örnek:

Buradaki hata "unutmak" değil, "düzeltmek değil" dir. Beklenmedik bir düzende gerçekleşen şeyler varsa, bir sorununuz olur. Etrafında çalışmayı denemek yerine onu çözmelisin (bir şeyi kilitlemek genellikle bir uğraştır).

Aktör modelini / mesajlaşmayı belirli bir dereceye adapte etmeye çalışmalı ve endişeleri birbirinden ayırmaya çalışmalısınız. Rolü Fooaçıkça bir tür HTTP iletişimini ele almaktır. Sisteminizi bunu paralel olarak yapacak şekilde tasarlamak istiyorsanız, nesne yaşam döngülerini ele alması ve buna göre senkronizasyona erişmesi gereken yukarıdaki katmandır.

Aynı değişken veri üzerinde çok sayıda ipliğin çalışmasını sağlamak zordur. Ama aynı zamanda nadiren gerekli. Bunu gerektiren tüm yaygın durumlar, daha yönetilebilir kavramlara çoktan getirilmiş ve herhangi bir ana zorunluluk dili için birçok kez uygulanmıştır. Sadece onları kullanmalısın.


2

Sorunlarınız oldukça kötü, ama tipik olarak C ++ 'nın kötü kullanımı. Kod incelemesi bu sorunlardan bazılarını çözecektir. 30 dakika içinde bir gözbebek seti sonuçların% 90'ını atar (bunun alıntıları googleabledir).

# 1 Sorun Kilitleme kilitlenmesini önlemek için sıkı bir kilitleme hiyerişi olduğundan emin olmanız gerekir.

Autolock'u bir sarıcı ve bir makro ile değiştirirseniz, bunu yapabilirsiniz.

Paketleyicinizin arkasında oluşturulan kilitlerin statik bir global haritasını tutun. Otomatik kilit sarıcı yapıcısına finename ve satır numarası bilgilerini eklemek için bir makro kullanırsınız.

Ayrıca statik bir baskın grafiğine de ihtiyacınız olacak.

Şimdi kilidin içinde, baskın grafiğini güncellemelisiniz ve bir sipariş değişikliği alırsanız bir hataya son verir ve iptal edersiniz.

Kapsamlı testlerden sonra gizli kilitlenmelerin çoğundan kurtulabilirsiniz.

Kod öğrenci için bir alıştırma olarak bırakılmıştır.

Sorun # 2 sonra gider (çoğunlukla)

Sizin mimarlık çözümünüz işe yarayacak. Daha önce misyon ve yaşam sistemi sistemlerinde kullandım. Benim aldığım şey bu

  • Değiştirilemez nesneleri geçirin veya geçmeden önce kopyalarını alın.
  • Verileri ortak değişkenler veya alıcılarla paylaşmayın.

  • Dış olaylar, bir iş parçacığı tarafından hizmet verilen bir sıraya çok iş parçacıklı bir gönderme yoluyla giriyor. Şimdi Olay işleme ile ilgili bir neden olabilir.

  • Çapraz iş parçacıklarının güvenli bir iş kuyruğuna girdiği veri değişiklikleri, bir iş parçacığı tarafından gerçekleştirilir. Abonelikler yapın. Artık veri akışlarıyla ilgili sebepleri sıralayabilirsiniz.

  • Verilerinizin şehirlerarası gitmesi gerekiyorsa, verileri veri kuyruğunda yayınlayın. Bu onu kopyalar ve zaman uyumsuz olarak abonelere iletir. Ayrıca programdaki tüm veri bağımlılıklarını kırar.

Bu hemen hemen ucuz bir oyuncu modeli. Giorgio'nun bağlantıları yardımcı olacak.

Sonunda, kapatma nesnelerindeki probleminiz.

Referans sayırken% 50 çözdünüz. Diğer% 50 geri sayımları yeniden yansıtmak içindir. Geri arama sahipleri bir refernce geçmek. Kapatma çağrısı daha sonra refcountta sıfır sayım beklemek zorundadır. Karmaşık nesne grafiklerini çözmez; Bu gerçek çöp koleksiyonuna giriyor. (Java'nın insana ne zaman gelip gelmeyeceğine dair herhangi bir vaatte bulunmadığı için motivasyon nedir () çağrılacak; sizi bu şekilde programlamanın dışına çıkarmak için.)


2

Gelecekteki kaşifler için: aktör modeli hakkındaki cevabı tamamlamak için içinde CSP'yi ( sıralı süreçleri iletmek ) eklemek istiyorum , içinde bulunduğu daha büyük süreç calculi ailesine bir selam vererek. CSP aktör modeline benzer, ancak farklı şekilde ayrıldı. Hala bir sürü iş parçacığınız var, ancak özellikle birbirleriyle değil, belirli kanallarla iletişim kuruyorlar ve her iki işlem de, her ikisinin de gerçekleşmeden önce göndermeye ve almaya hazır olmalıdır. Bir de var resmiyet dil CSP kodunun doğru kanıtlayan için. Hala yoğun bir şekilde CSP kullanmaya geçiyorum, ancak birkaç aydır birkaç projede kullanıyorum, ve şimdi çok basitleştirilmiş şeyler.

Kent Üniversitesi C ++ uygulamasına sahiptir ( https://github.com/themasterchef/cppcsp2 adresinde klonlanan https://www.cs.kent.ac.uk/projects/ofa/c++csp/ ).


1

Konu etrafındaki tasarım desenleri üzerine literatür veya yazılar Mutekslere ve semaforlara girişin ötesinde bir şey. Eşzamanlı paralelliğe de ihtiyacımız yok, sadece diğer konuların eşzamansız olaylarını doğru bir şekilde ele almak için bir nesne modeli tasarlamanın yolları.

Şu anda bu yazıyı okuyorum ve elde edebileceğiniz tüm problemleri ve bunlardan nasıl kaçınılacağını açıklıyor, C ++ 'da (yeni iş parçacığı kütüphanesini kullanarak ama genel açıklamalar sizin durumunuz için geçerli olduğunu düşünüyorum): http: //www.amazon. com / C-eşzamanlılık-Eylem-Pratik -Multithreading / dp / 1933988770 / ref = sr_1_1? ie = UTF8 & qid = 1337934534 & sr = 8-1

Çeşitli bileşenlerin iş parçacığını şemalandırmanın yolları, böylece çözümlemesi kolay çözümler geliştirilir. (Yani, nesneler ve sınıflar arasında iş parçacıklarını tartışmak için bir UML'ye eşdeğerdir)

Şahsen basitleştirilmiş bir UML kullanıyorum ve mesajların zaman uyumsuz olarak yapıldığını varsayıyorum. Ayrıca, bu "modüller" arasında geçerlidir ancak modüller içinde bilmek istemiyorum.

Geliştirme ekibinizi çok iş parçacıklı kod içeren konular hakkında eğitin.

Kitap yardımcı olacaktır, ancak alıştırmaların / prototiplerin ve deneyimli akıl hocasının daha iyi olacağını düşünüyorum.

Sen ne yapardın?

İnsanların projede eşzamanlılık sorunlarını anlamadıklarından tamamen kaçınırdım. Ama sanırım bunu yapamazsınız, bu nedenle, kendi durumunuzda, ekibin daha eğitimli olduğundan emin olmaya çalışmaktan başka hiçbir fikrim yok.


Kitap önerisi için teşekkürler. Büyük ihtimalle onu alacağım.
koncurrency

Diş açmak gerçekten zor. Her programcı zor değil. İş dünyasında, kullanılan iplikleri her gördüğümde, aynı anda iki ipliğin akamayacağı şekilde kilitlerle çevriliydiler. Kolaylaştırmak için izleyebileceğiniz kurallar var, ancak yine de zor.
GlenPeterson 12:12

@GlenPeterson Anladım, şimdi daha fazla deneyime sahibim (bu cevabından beri) Yönetilebilir hale getirmek ve veri paylaşımını engellemek için daha iyi soyutlamalara ihtiyacımız olduğunu biliyorum. Neyse ki, dil tasarımcıları bu konuda çok çalışıyor gibi görünüyor.
Klaim

Scala'dan gerçekten etkilendim, özellikle değişkenliğin işlevsel programlama faydalarını, Java'nın C ++ 'ın doğrudan soyundan olan minimal yan etkileri olan. Java Sanal Makinesi üzerinde çalışır, bu yüzden ihtiyacınız olan performans olmayabilir. Joshua Bloch'un kitabı, "Etkili Java", değişkenliği en aza indirgemek, hava geçirmez arayüzler yapmak ve iplik emniyetini içerir. Java tabanlı olmasına rağmen, bunun% 80-90'ını C ++ 'a uygulayabileceğinizi iddia ediyorum. Değişkenliği ve paylaşılan durumu (veya paylaşılan durumun değişebilirliğini) kod incelemelerinizde sorgulamak sizin için iyi bir ilk adım olabilir.
GlenPeterson 14:12

1

Sorunu kabul ederek ve aktif olarak bir çözüm arayarak zaten yoldasınız. İşte ne yapardım:

  • Oturun ve uygulamanız için bir iş parçacığı modeli tasarlayın. Bu, aşağıdaki gibi soruları cevaplayan bir belgedir: Hangi tür konulara sahipsiniz? Hangi konuda ne yapılmalı? Hangi tür senkronizasyon modellerini kullanmalısınız? Başka bir deyişle, çok iş parçacıklı problemlerle savaşırken "angajman kurallarını" tanımlamalıdır.
  • Kod tabanınızı hatalara karşı kontrol etmek için iş parçacığı analiz araçlarını kullanın. Valgrind, Helgrind adında bir iş parçacığı denetleyicisine sahiptir; bu, paylaşılan durumun uygun senkronizasyon olmadan manipüle edilmesi gibi şeyleri tespit etmede iyidir. Dışarıda kesinlikle daha başka güzel araçlar var, gidip onları arayın.
  • C ++ 'dan uzaklaşmayı düşünün. C ++ eşzamanlı programlar yazmak için bir kabustur. Kişisel tercihim olurdu Erlang , ama bu bir zevk meselesi.

8
Son bit için kesinlikle -1. OP'nin kodunun gerçek C ++ araçlarını değil en ilkel araçları kullandığı görülüyor.
DeadMG

2
Katılmıyorum C ++ eşzamanlılığı, uygun C ++ mekanizmalarını ve araçlarını kullanıyor olsanız bile bir kabustur. Ve lütfen " dikkate al " ifadesini seçtiğime dikkat edin . Bunun gerçekçi bir alternatif olamayacağını tamamen biliyorum, ancak alternatifleri göz önünde bulundurmadan C ++ ile kalmak sadece aptalca.
JesperE

4
@JesperE - üzgünüm ama hayır. C ++ 'daki eşzamanlılık, sadece çok düşük seviyeye inerek bir kabus olur. Uygun bir iş parçacığı soyutlama kullanın ve başka bir dilden veya çalışma zamanından daha kötü değil. Doğru uygulama yapısı sayesinde, şimdiye kadar gördüğüm her şey kadar kolay.
Michael Kohne

2
Çalıştığım yerde uygun bir uygulama yapısına sahip olduğumuza, doğru iş parçacığı soyutlamalarını kullandığımıza vb. İnanıyorum. Buna rağmen, sadece eşzamanlılık için uygun şekilde tasarlanmış dillerde görünmeyen hataları ayıklamak için yıllarca zaman harcadık. Ancak, bu konuda hemfikir olmamayı kabul edeceğimizi hissediyorum.
JesperE

1
@JesperE: Sana katılıyorum. Erlang modeli (bunun için Scala / Java, Ruby ve bildiğim kadarıyla C ++ için de uygulamaları var), doğrudan konulara kodlamaktan çok daha sağlam.
Giorgio

1

Örneğinize bakın: Foo :: Shutdown çalışmaya başlar başlamaz, artık OnHttpRequestComplete'i çalıştırmak mümkün olamaz. Bunun herhangi bir uygulamayla ilgisi yok, işe yaramaz.

OnHttpRequestComplete çağrısı devam ederken (kesinlikle doğru) ve muhtemelen OnHttpRequestComplete çağrısı hala üstündeyse, Foo :: Shutdown'ın çağrılabilir olmaması gerektiğini de iddia edebilirsiniz.

Doğru olan ilk şey kilitleme vb. Değil, izin verilip verilmediğinin mantığıdır. Basit bir model, sınıfınızın sıfır ya da daha fazla eksik isteği, henüz çağrılmayan sıfır ya da daha fazla tamamlamayı, çalışmakta olan sıfır ya da daha fazla tamamlamayı ve nesnenizin kapanmasını isteyip istemediği olabilir.

Foo :: Shutdown'ın tamamlama işlemlerini bitirmesi, eksik istekleri, mümkünse kapanabilecekleri bir noktaya getirmesi, daha fazla tamamlamanın başlatılmasına izin vermemesi, daha fazla isteğin başlatılmasına izin vermemesi beklenir.

Yapmanız gerekenler: Tam olarak ne yapacaklarını söyleyerek işlevlerinize özellikler ekleyin . (Örneğin, kapatma isteğinin çağrılmasından sonra bir http isteğinin başlatılması başarısız olabilir). Sonra işlevlerinizi yazın, böylece özellikleri yerine getirsinler.

Kilitler, yalnızca paylaşılan değişkenlerin değiştirilmesini kontrol etmek için mümkün olan en küçük süre boyunca kullanılır. Bu nedenle, bir Kilit tarafından korunan bir değişken “performShutDown” olabilir.


0

Sen ne yapardın?

Dürüst olmak gerekirse; Çabucak kaçardım.

Eşzamanlılık sorunları NASTY . Bir şeyler aylar boyunca mükemmel bir şekilde çalışabilir ve sonra (birkaç şeyin belirli zamanlaması nedeniyle) müşterinin yüzüne aniden patlar, ne olduğunu çözmenin bir yolu yoktur, güzel (tekrarlanabilir) bir hata raporu görmenin ve hiçbir şekilde görmenin umuduyla olmaz Yazılımla ilgisi olmayan bir donanım sorunu olmadığından emin olmak için.

Eşzamanlılık sorunlarından kaçınmak, tasarım aşamasında, tam olarak nasıl yapacağınızla ("küresel kilit düzeni", oyuncu modeli, ...) başlayarak başlamalıdır. Bu, yaklaşan bir sürümden sonra her şeyin kendi kendini yok etmeyeceği umuduyla çılgınca bir panik içinde düzeltmeye çalıştığınız bir şey değil.

Burada şaka yapmadığımı unutmayın. Kendi sözleriniz (" Ekibi terk eden diğer geliştiricilerin kaynaklarından geliyor. Takımdaki mevcut geliştiriciler çok zeki, ancak çoğunlukla deneyim açısından küçükler. "), İnsanların zaten benim yaptığım tüm deneyimleri yaptığını belirtiyor . öneriyorum.

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.