Kötü çoklu kullanım nedeniyle neredeyse / aslında başarısız olan bir projeden hangi dersleri öğrendiniz? [kapalı]


11

Kötü çoklu kullanım nedeniyle neredeyse / aslında başarısız olan bir projeden hangi dersleri öğrendiniz?

Bazen, çerçeve, işleri büyüklük sırasını düzeltmeyi zorlaştıran belirli bir diş açma modeli uygular.

Bana gelince, henüz son başarısızlıktan kurtuldum ve bu çerçevede çoklu okuma ile ilgili hiçbir şey üzerinde çalışmamamın daha iyi olduğunu hissediyorum.

Basit çatal / birleştirme olan ve verilerin yalnızca bir yönde gittiğini (sinyaller dairesel bir yönde gidebilirken) çok iş parçacıklı problemlerde iyi olduğumu buldum.

Bazı çalışmaların yalnızca kesin olarak serileştirilmiş bir iş parçacığı ("ana iş parçacığı") ve diğer iş yalnızca ana iş parçacığı ("işçi iş parçacığı") ancak herhangi bir iş parçacığı üzerinde yapılabilir GUI işlem yapamıyorum ve burada veriler ve mesajlar N bileşenleri (tümüyle bağlı bir grafik) arasında her yöne gitmek zorundadır.

O projeyi başka bir proje için terk ettiğim zaman, her yerde kilitlenme sorunları vardı. 2-3 ay sonra, diğer birkaç geliştiricinin müşterilere gönderilebilecek noktaya kadar tüm kilitlenme sorunlarını çözmeyi başardığını duydum. Hiç eksik olduğum bilgi parçasını öğrenemedim.

Proje hakkında bir şey: ileti kimliği sayısı (iş parçacığına bakılmaksızın başka bir nesnenin ileti kuyruğuna gönderilebilen bir olayın anlamını tanımlayan tamsayı değerleri) binlerce olur. Benzersiz dizeler (kullanıcı iletileri) de yaklaşık bin kişiyi bulur.

Katma

Başka bir takımdan aldığım en iyi benzetme (geçmiş veya şimdiki projelerimle ilgisi olmayan) "verileri bir veritabanına koymak" idi. ("Veritabanı", merkezileştirme ve atom güncellemelerine atıfta bulunur.) Birden çok görünüme bölünmüş bir GUI'de, hepsi aynı "ana iş parçacığı" üzerinde çalışır ve GUI olmayan tüm ağır kaldırma işlemleri, bağımsız çalışan iş parçacıklarında yapılır. Veritabanı gibi davranan tek bir düzlükte saklanmalı ve "Veritabanı" nın önemsiz olmayan veri bağımlılıklarını içeren tüm "atomik güncelleştirmeleri" işlemesine izin vermelisiniz. GUI'nin diğer tüm bölümleri ekran çizimini ve başka bir şeyi işlemez. Kullanıcı arabirimi parçaları bir şeyleri önbelleğe alabilir ve kullanıcı düzgün bir şekilde tasarlandıysa bir saniye boyunca eskiyse fark etmez. Bu "veritabanı" aynı zamanda "belge" olarak da bilinir Belge Görünümü mimarisinde. Maalesef - hayır, uygulamam aslında tüm verileri Views'da depolar. Neden böyle olduğunu bilmiyorum.

Katkıda bulunanlar:

(Katkıda bulunanların gerçek / kişisel örnekler kullanmasına gerek yoktur. Eğer kendiniz güvenilir olduğuna karar verilirse, fıkra örnekleri ile ilgili dersler de kabul edilir.)



Bence 'konuları düşünebilmenin' bir yetenek ve daha iyi ifade eksikliği nedeniyle öğrenilebilecek daha az şey olduğunu düşünüyorum. Paralel sistemlerle uzun süredir çalışan birçok geliştirici tanıyorum, ancak verilerin birden fazla yöne gitmesi gerekiyorsa boğuluyorlar.
dauphic

Yanıtlar:


13

En sevdiğim ders - çok zor kazandı! - Çok iş parçacıklı bir programda, zamanlayıcının sizden nefret eden gizli bir domuz olmasıdır. Eğer işler ters gidebilirse, yapacaklar ama beklenmedik bir şekilde. Herhangi bir yanlış yapın ve garip heisenbug'ları kovalayacaksınız (çünkü eklediğiniz herhangi bir enstrümantasyon zamanlamaları değiştirecek ve size farklı bir çalışma deseni verecektir).

Bunu düzeltmenin tek aklı yolu, tüm iş parçacığı işlemlerini, her şeyi doğru yapan ve kilitlerin düzgün bir şekilde tutulduğundan emin olmak için çok muhafazakar olan bir kod parçasına sıkıca bağlamaktır (ve küresel olarak sürekli bir edinme sırası ile). . Bunu yapmanın en kolay yolu, zaman uyumsuz olması gereken mesajlaşma haricinde iş parçacıkları arasında bellek (veya başka kaynaklar) paylaşmamaktır ; diğer her şeyi iş parçacığı olmayan bir tarzda yazmanıza olanak tanır. (Bonus: Bir kümede birden çok makineye ölçeklendirmek çok daha kolaydır.)


+1, "eşzamansız olması gereken mesajlaşma dışındaki ileti dizileri arasında bellek (veya diğer kaynakları) paylaşmama";
Nemanja Trifunovic

1
Sadece yol? Değişmez veri türleri ne olacak?
Aaronaught

is that in a multithreaded program the scheduler is a sneaky swine that hates you.- hayır değil, tam olarak ne dediğini yapar :)
mattnz

@Aaronaught: Değişmez olsa bile, referans olarak geçen küresel değerler hala küresel GC gerektirir ve bu da bir sürü küresel kaynağı yeniden üretir. İş parçacığı başına bellek yönetimini kullanabilmek güzeldir, çünkü bir dizi küresel kilitten kurtulmanızı sağlar.
Donal Üyeleri

Temel olmayan türlerin değerlerini referans olarak geçiremezsiniz, ancak daha yüksek düzeyde kilitleme gerektirmesi (örneğin, bir mesaj geri dönene kadar bir referans tutan “sahip”, bakımda karıştırılması kolaydır) veya sahiplik aktarmak için mesajlaşma motorundaki karmaşık kod. Ya da her şey marshal ve diğer iş parçacığı içinde mareşal, çok daha yavaş (yine de bir kümeye giderken bunu yapmak zorunda). Takip etmek ve hafızayı hiç paylaşmamak daha kolaydır.
Donal Üyeleri

6

İşte şu anda düşünebileceğim birkaç temel ders (başarısız projelerden değil, gerçek projelerde görülen gerçek konulardan):

  • Paylaşılan bir kaynağı tutarken aramaları engellemekten kaçının. Yaygın kilitlenme deseni iplik muteks kapmak, bir geri arama yapar, aynı muteks üzerinde geri arama blokları.
  • Mutex / kritik bölüm ile paylaşılan veri yapılarına erişimi koruyun (veya kilitsiz olanları kullanın - ancak kendinizinkini icat etmeyin!)
  • Atomisite varsaymayın - atomik API'ler kullanın (örn. InterlockedIncrement).
  • Kullandığınız kitaplıkların, nesnelerin veya API'lerin iş parçacığı güvenliği ile ilgili RTFM.
  • Olaylar, semaforlar gibi mevcut senkonizasyon ilkellerinden yararlanın. (Ama onları kullanırken iyi durumda olduğunuzu bildiğinize çok dikkat edin - Yanlış durumda bildirilen olayların veya verilerin kaybolabileceği birçok örnek gördüm)
  • İş parçacıklarının aynı anda ve / veya herhangi bir sırada çalışabileceğini ve bu bağlamın iş parçacıkları arasında herhangi bir zamanda değişebileceğini varsayalım (diğer garantileri yapan bir işletim sistemi altında değilse).

6
  • Tüm GUI projeniz yalnızca ana iş parçacığından çağrılmalıdır . Temel olarak, GUI'nize tek bir (.net) "çağırma" koymamalısınız. Birden çok iş parçacığı, daha yavaş veri erişimini işleyen ayrı projelerde kalmalıdır.

GUI projesinin bir düzine iş parçacığı kullandığı bir bölümü miras aldık. Sorunlardan başka bir şey vermiyor. Kilitlenmeler, yarış sorunları, çapraz iplik GUI çağrıları ...


"Proje", "montaj" anlamına mı geliyor? Sınıfların meclisler arasındaki dağılımının diş açma sorunlarına nasıl yol açacağını görmüyorum.
nikie

Projemde bu gerçekten bir meclis. Ancak asıl nokta, bu klasörlerdeki tüm kodların istisna olmaksızın ana evreden çağrılması gerektiğidir.
Carra

Bu kuralın genellikle geçerli olduğunu düşünmüyorum. Evet, GUI kodunu asla başka bir iş parçacığından aramamalısınız. Ancak sınıfları klasörlere / projelere / derlemelere nasıl dağıtacağınız bağımsız bir karardır.
nikie

1

Java 5 ve sonraki sürümlerde, çoklu iş parçacıklı çatal birleştirme stili programlarını işlemek için hayatı kolaylaştırmayı amaçlayan Yürütücüler bulunur.

Bunları kullanın, çok fazla acıyı kaldıracaktır.

(ve evet, bunu bir projeden öğrendim :))


1
Bu yanıtı diğer dillere uygulamak için - mümkün olduğunda bu dil tarafından sağlanan yüksek kaliteli paralel işleme çerçevelerini kullanın. (Ancak, bir çerçevenin gerçekten harika ve son derece kullanışlı olup olmadığını sadece zaman gösterecektir.)
rwong

1

Zor gerçek zamanlı gömülü sistemlerde bir geçmişim var. Çoklu kullanımdan kaynaklanan sorunların olup olmadığını test edemezsiniz. (Bazen varlığı doğrulayabilirsiniz). Kodun doğru olması gerekir. Bu nedenle, tüm iplik etkileşimleri etrafında en iyi uygulama.

  • # 1 kuralı: ÖPÜCÜK - İpliğe ihtiyacınız yoksa, döndürmeyin. Mümkün olduğunca seri hale getirin.
  • # 2 kuralı: # 1'i kırmayın.
  • # 3 Eğer gözden geçirme yoluyla kanıtlayamazsanız, doğru değil.

Kural 1 için +1. Başlangıçta başka bir iş parçacığı tamamlanana kadar engelleyecek bir proje üzerinde çalışıyordum - aslında bir yöntem çağrısı! Neyse ki, bu yaklaşıma karşı karar verdik.
Michael K

# 3 FTW. Kilit zamanlama şemaları ile uğraşarak saatler harcamak ya da neden bazen parçalandığını merak etmenin aylardan daha iyi olduğunu kanıtlamak için ne kullanırsanız kullanın.

1

Geçen yıl aldığım multithreading dersinden bir analoji çok yardımcı oldu. Diş senkronizasyonu, bir kavşağın (veri) iki araba (diş) tarafından aynı anda kullanılmasını önleyen bir trafik sinyali gibidir. Birçok geliştiricinin yaptığı hata, bir arabanın geçmesine izin vermek için şehrin çoğunda ışıkları kırmızıya çevirmek çünkü ihtiyaç duydukları sinyali tam olarak anlamanın çok zor veya tehlikeli olduğunu düşünüyorlar. Trafik az olduğunda bu işe yarayabilir, ancak uygulamanız büyüdükçe gridlock'a yol açacaktır.

Bu teoride zaten bildiğim bir şeydi, ancak bu dersten sonra analoji bana gerçekten yapıştı ve bundan sonra ne kadar sıklıkla bir iş parçacığı sorununu araştıracağım ve bir değişken yazarken her yerde devre dışı bırakılan kesintileri bu kadar şaşırdım. sadece iki iplik kullanılır veya muteksler, tamamen önlemek için yeniden düzenlenebilirken uzun süre tutulur.

Başka bir deyişle, en kötü iş parçacığı sorunlarından bazıları, iş parçacığı sorunlarından kaçınmaya çalışan aşırı yüklenmeden kaynaklanır.


0

Tekrar yapmayı deneyin.

En azından benim için bir fark yaratan şey pratikti. Çok dişli ve dağıtılmış işleri birkaç kez yaptıktan sonra asın.

Bence hata ayıklama gerçekten zor olan şeydir. VS kullanarak çok iş parçacıklı kod hata ayıklayabilirsiniz ama gdb kullanmak zorunda kalırsanız gerçekten tam bir kayıp var. Benim hatam, muhtemelen.

Hakkında daha fazla bilgi edinen başka bir şey de kilitli veri yapılarıdır.

Çerçeveyi belirtirseniz bu sorunun gerçekten geliştirilebileceğini düşünüyorum. Örneğin, .NET iş parçacığı havuzları ve arka plan çalışanları QThread'den gerçekten farklıdır. Her zaman platforma özgü birkaç yaka vardır.


Herhangi bir çerçeveden öyküleri duymakla ilgileniyorum, çünkü her çerçeveden öğrenilecek şeyler, özellikle de maruz kalmadığım şeyler olduğuna inanıyorum.
rwong

1
hata ayıklayıcılar çok iş parçacıklı bir ortamda büyük ölçüde işe yaramaz.
Pemdas

Zaten sorunun ne olduğunu söyleyen, ancak çözmeme yardımcı olmayacak çok iş parçacıklı yürütme izleyicilerim var. Benim sorunumun temel noktası, "mevcut tasarıma göre, X mesajını Y nesnesine bu şekilde (sıra) geçiremiyorum; dev bir kuyruğa eklenmesi gerekiyor ve sonunda işlenecek; ama bundan dolayı , iletilerin kullanıcıya doğru zamanda görünmesinin bir yolu yoktur - her zaman anakronistik olarak gerçekleşir ve kullanıcıyı çok, çok karışık hale getirir.Gelmesi gereken yerlere ilerleme çubukları, iptal düğmeleri veya hata mesajları eklemeniz gerekebilir. onlara sahip değil . "
rwong

0

Alt seviye modüllerden yüksek seviye modüllere geri çağrıların büyük bir kötülük olduğunu öğrendim, çünkü kilitleri ters sırada elde etmeye neden oluyorlar.


Geri aramalar kötü değildir ... iplik kopuşundan başka bir şey yapmaları muhtemelen kötülüğün köküdür. Sadece mesaj kuyruğuna bir jeton göndermeyen herhangi bir geri arama şüphesi olacaktır.
Pemdas

Bir optimizasyon probleminin çözülmesi (f (x) simge durumuna küçültmek gibi) genellikle optimizasyon prosedürüne işaretçi f (x) için bir işaretçi sağlayarak minimum değeri ararken "geri çağırır". Geri arama yapmadan nasıl yapardınız?
quant_dev

1
Yok oy yok, ancak geri aramalar kötü değil. Bir kilidi tutarken geri arama yapmak kötüdür. Kilitlenebileceğini veya bekleyebileceğini bilmediğinizde, bir kilidin içinde hiçbir şey çağırmayın. Bu sadece geri çağrıları değil, aynı zamanda sanal fonksiyonları, API fonksiyonlarını, diğer modüllerdeki fonksiyonları ("daha yüksek seviye" veya "daha düşük seviye") içerir.
nikie

@nikie: Geri arama sırasında bir kilit tutulması gerekiyorsa, API'nin geri kalanının yeniden girilecek şekilde tasarlanması (sabit!) veya bir kilidi tuttuğunuz gerçeğinin API'nin belgelenmiş bir parçası olması gerekir ( talihsiz, ama bazen tüm yapabileceğiniz).
Donal Üyeleri

@Donal Fellows: Bir geri arama sırasında bir kilit tutulması gerekiyorsa, bir tasarım kusurunuz olduğunu söyleyebilirim. Gerçekten başka bir yol yoksa, evet, elbette bunu belgeleyin! Tıpkı geri aramanın arka plan iş parçacığında çağrılıp çağrılmayacağını belgeleyeceğiniz gibi. Bu arayüzün bir parçası.
nikie
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.