GUI programlamada iplik güvenliğini sağlamak neden arayan kişinin sorumluluğundadır?


37

Birçok yerde, UI bileşenlerini güncellerken UI iş parçacığında olmanızı sağlamak için arayanın sorumluluğunda olduğu konusunda kanonik bir bilgelik 1 olduğunu gördüm (özellikle, Java Swing'de, Olay Gönderme Konusunda olduğunuzu ) .

Bu neden böyle? Olay Gönderme İş Parçası, MVC / MVP / MVVM'deki görüşün bir endişe kaynağıdır ; onu her yerde ele almak ancak görünüm, uygulamanın uygulanmasıyla o görüntünün uygulamadaki iş parçacığı modeli arasında sıkı bir bağlantı oluşturur .

Özellikle, Swing kullanan MVC mimarisine sahip bir uygulamam olduğunu varsayalım. Arayan, Olay Gönderme İpindeki bileşenleri güncellemekten sorumluysa, Swing View uygulamamı bir JavaFX uygulaması için değiştirmeye çalışırsam, bunun yerine JavaFX Uygulaması iş parçacığını kullanmak için tüm Presenter / Controller kodunu değiştirmeliyim .

Sanırım iki sorum var:

  1. UI bileşeni iplik emniyetini sağlamak neden arayanın sorumluluğundadır? Yukarıdaki akıl yürütmedeki kusur nerede?
  2. Uygulamamı, bu diş güvenliği kaygılarının gevşek bir şekilde birleşmesine rağmen yine de diş ipliği açısından güvenli olacak şekilde nasıl tasarlayabilirim?

"Arayan sorumlusu" ile ne demek istediğimi göstermek için bazı MCVE Java kodları ekleyeyim (burada yapamadığım ama mümkün olan en az düzeyde olmaya çalışarak bazı iyi uygulamalar var):

Arayan sorumlu olmak:

public class Presenter {
  private final View;

  void updateViewWithNewData(final Data data) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        view.setData(data);
      }
    });
  }
}
public class View {
  void setData(Data data) {
    component.setText(data.getMessage());
  }
}

Sorumlu olduğunu görüntüle:

public class Presenter {
  private final View;

  void updateViewWithNewData(final Data data) {
    view.setData(data);
  }
}
public class View {
  void setData(Data data) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        component.setText(data.getMessage());
      }
    });
  }
}

1: Bu gönderinin yazarı, Swing on Stack Overflow'ta en yüksek etiket puanına sahip. Her yerde bunu söylüyor ve diğer yerlerde de arayanın sorumluluğu olduğunu gördüm.


1
Eh, performans IMHO. Bu etkinlik ilanları ücretsiz gelmez ve önemsiz uygulamalarda, sayılarını en aza indirmek ister (ve hiçbirinin çok büyük olmadığından emin olun), ancak sunumda bu küçültme / yoğunlaştırma mantıklı olarak yapılmalıdır.
Ordous

1
@Ordous İplik geçişini görünümde tutarken kayıtların minimum düzeyde olmasını sağlayabilirsiniz.
durron597

2
Bir süre önce bu konuyu tartışan gerçekten iyi bir blog okudum, temelde söylediği: UI kit ipliğini güvenli hale getirmenin çok tehlikeli olduğu, olası kilitlenmeleri ortaya çıkardığı ve yarış koşullarının nasıl uygulandığına bağlı olarak. çerçeve. Bir performans değerlendirmesi de var. Çok fazla değil, ama Swing ilk piyasaya sürüldüğü zaman, performansı (kötü) nedeniyle yoğun bir şekilde eleştirildi, ama bu gerçekten Swing'in hatası değildi, insanların nasıl kullanılacağına dair bilgi eksikliğiydi.
MadProgrammer

1
SWT, ihlaliniz durumunda istisnalar atarak iplik güvenliği kavramını zorlar, ama hoş değil, ama en azından bunun farkındasınız. Swing’ten JavaFX’e geçişten bahsediyorsunuz, ancak bu problemin hemen hemen her UI çerçevesiyle ilgili olacağını düşünüyorsunuz, Swing sadece sorunu vurguluyor gibi görünüyor. UI'ye yapılan çağrıların doğru şekilde senkronize edilmesini sağlamak olan, ara katmanı (kontrolörün kontrolörü?) Tasarlayabilirsiniz.
API'nizin

1
ve çoğu geliştirici, UI API'sine uygulanan herhangi bir iş parçacığı korumasının kısıtlayıcı olması ya da ihtiyaçlarını karşılamamasından şikayetçi olacaktı. Daha size ihtiyaçlarına göre bu sorunu çözmek istiyorum nasıl karar için izin vermek
MadProgrammer

Yanıtlar:


22

Başarısız olduğu rüya denemesinin sonuna doğru , Graham Hamilton (büyük bir Java mimarı), geliştiricilerin "bir denklemi bir olay sırası modeliyle koruyacaklarını, açık olmayan çeşitli kurallara uymaları gerekip gerekmediğini" görülebilir ve açık bir şekilde kullanmaları gerektiğini söyler. olay kuyruğu modeli "insanların modeli daha güvenilir şekilde takip etmelerine ve böylece güvenilir çalışan GUI programlarının oluşturulmasına yardımcı oluyor" gibi görünüyor.

Başka bir deyişle, bir olay kuyruğu modelinin üzerine çok iş parçacıklı bir cephe yerleştirmeye çalışırsanız, soyutlama zaman zaman hata ayıklanması son derece zor olan açık olmayan yollardan sızacaktır. Kağıt üzerinde çalışacak gibi görünüyor, ancak üretimde parçalanıyor.

Tek bileşenlerin etrafına küçük sargılar eklemek muhtemelen bir iş parçacığındaki ilerleme çubuğunu güncellemek gibi sorunlu olmayacak. Birden çok kilit gerektiren daha karmaşık bir şey yapmaya çalışırsanız, çok iş parçacıklı katmanın ve olay sırası katmanının nasıl etkileşimde bulunduğunu düşünmek gerçekten zorlaşmaya başlar.

Bu tür sorunların tüm GUI araç kitleri için evrensel olduğunu unutmayın. Sizin sunucu bir olay gönderme modeli varsayarsak / kontrolör sıkıca size bağlanması değil yalnızca belirli bir GUI Araç setinin eşzamanlılık modeli, bu sizi birleştirilmesi oluyor için hepsini . Olay kuyruğu arayüzü soyutlanması zor olmamalıdır.


25

Çünkü GUI lib thread'ı güvenli hale getirmek büyük bir baş ağrısı ve bir tıkanıklıktır.

GUI'lerdeki kontrol akışı, genellikle olay kuyruğundan kök pencereye, gui widget'larına ve uygulama kodundan kök pencereye yayılan widget'a kadar 2 yönde gider.

Kök penceresini kilitlemeyen bir kilitleme stratejisi geliştirmek (çok fazla çekişmeye neden olur) zordur . Başka bir iplik yukarıdan aşağıya kilitlenirken tabanın kilitlenmesi, anında kilitlenme elde etmek için harika bir yoldur.

Ve mevcut iş parçacığının gui iş parçacığı olup olmadığını kontrol etmek her zaman maliyetlidir ve özellikle bir okuma güncellemesi yazma sırası yaparken, gui ile gerçekte neler olduğu konusunda kafa karışıklığına neden olabilir. Bu, yarışlardan kaçınmak için verilerde kilitlenmeye ihtiyaç duyar.


1
İlginç bir şekilde, bu sorunu, iş parçacığı aktarımını yöneten yazdığım güncellemeler için bir kuyruklama çerçevesi oluşturarak çözüyorum.
durron597

2
@ durron597: Başka bir iş parçacığının etkileyebileceği kullanıcı arayüzünün mevcut durumuna bağlı olarak hiçbir güncellemeniz yok mu? O zaman işe yarayabilir.
Deduplicator

Neden yuvalanmış kilitlere ihtiyacınız olsun istiyorsunuz? Alt pencerede ayrıntılar üzerinde çalışırken neden tüm kök penceresini kilitlemeniz gerekiyor? Kilit sıralaması, çoklu kilitleri doğru sırayla kilitlemek için tek bir açık yöntemle çözülebilir bir problemdir (yukarıdan aşağıya ya da aşağıdan yukarıya, ancak seçimi arayan
tarafa

1
Kök ile @ MSalters Mevcut pencereyi kastettim. Edinmeniz gereken tüm kilitleri alabilmeniz için, karşınıza çıkan her kabın kilitlenmesini gerektiren hiyerarşide yürümeniz gerekir (üst kısmı kilitlemenizi sağlamak için) ve üstünün kilitlenmesini sağlayın. Kök penceresini aldıktan sonra kilitleme işlemini yaparsınız.
cırcır ucube

@ ratchetfreak: Kilitlemeye çalıştığınız çocuk kilitlediğiniz sırada başka bir iplik tarafından çıkarılırsa, bu sadece biraz talihsiz ancak kilitlemeyle ilgili değildir. Az önce başka bir iş parçacığının kaldırdığı bir nesne üzerinde işlem yapamazsınız. Ama neden o hala, hala ipliğin kullandığı nesneleri / pencereleri kaldıran o iplik? Bu, herhangi bir senaryoda iyi değil, yalnızca UI'lerde değil.
MSalters,

17

Dişli (ortak hafıza modelinde), soyutlama çabalarına meydan okuma eğiliminde olan bir özelliktir. Basit bir örnek bir olduğunu Settipi: iken Contains(..)ve Add(...)ve Update(...)tek dişli senaryoda mükemmel geçerli API, çok dişli senaryo bir ihtiyacı olduğunu AddOrUpdate.

Aynı şey UI için de geçerlidir - listenin üzerindeki öğelerin sayısının bulunduğu öğelerin bir listesini görüntülemek istiyorsanız, her değişiklikte ikisini de güncellemeniz gerekir.

  1. Araç takımı bu sorunu çözemez çünkü kilitleme işlem sırasının doğru kaldığından emin olmaz.
  2. Görünüm sorunu çözebilir, ancak yalnızca iş kurallarının listenin üzerindeki sayının listedeki öğelerin sayısıyla eşleşmesi ve listenin yalnızca görünüm üzerinden güncellenmesi gerektiğine izin vermesi durumunda. MVC'nin olması gerektiği gibi değil.
  3. Sunucu bunu çözebilir, ancak görünümün iş parçacığı için özel ihtiyaçları olduğunu bilmesi gerekir.
  4. Çok iş parçacıklı yetenekli bir modele veri bağlama başka bir seçenektir. Fakat bu, modeli UI ile ilgili olması gereken şeylerle zorlaştırıyor.

Bunların hiçbiri gerçekten cazip görünüyor. Sunucunun iş parçacığı işlemesinden sorumlu hale getirilmesi, iyi olduğu için değil, çalıştığı ve alternatiflerin daha kötü olduğu için tavsiye edilir.


Belki Görünüm ile Sunucunun arasına da yerleştirilebilecek bir katman yerleştirilebilir.
durron597

2
.NET, System.Windows.Threading.Dispatcherhem WPF'ye hem de WinForms UI-Threads öğesine gönderme işleve sahiptir. Sunucu ile görünüm arasındaki bu tür bir katman kesinlikle faydalıdır. Ancak bağımsızlık sağlamak için sadece araç takımı bağımsızlığı sağlar.
Patrick

9

Bir süre önce bu konuyu (Karl Bielefeldt'in belirttiği gibi) tartışan gerçekten iyi bir blog okudum, temelde söylediği, UI kit ipliğini güvenli hale getirmenin ve olası kilitlenmelere neden olduğu için güvenli bir şekilde denemenin çok tehlikeli olduğudur. uygulandı, yarış koşulları çerçeveye eklendi.

Bir performans değerlendirmesi de var. Çok fazla değil, ama Swing ilk piyasaya sürüldüğü zaman, performansı (kötü) nedeniyle yoğun bir şekilde eleştirildi, ama bu gerçekten Swing'in hatası değildi, insanların nasıl kullanılacağına dair bilgi eksikliğiydi.

SWT, ihlaliniz durumunda istisnalar atarak iplik güvenliği kavramını zorlar, ama hoş değil, ama en azından farkında olursunuz.

Boyama sürecine bakarsanız, örneğin, elemanların boyandığı sıra çok önemlidir. Bir bileşenin boyanmasının, ekranın diğer bölümlerinde yan etkisi olmasını istemezsiniz. Bir etiketin text özelliğini güncelleyebildiğinizi, ancak iki farklı iş parçacığı tarafından boyandığını, bozuk bir çıktıyla sonuçlanabileceğini hayal edin. Bu nedenle, tüm boyamalar normalde ihtiyaçlar / talepler sırasına göre (tek seferde gerçek fiziksel boya döngülerini azaltmak için yoğunlaştırılmış) tek bir iplik içinde yapılır

Swing’ten JavaFX’e geçişten bahsettiniz, ancak bu sorunu hemen hemen her UI çerçevesiyle (sadece kalın istemciler değil, web de dahil olmak üzere) alırsınız, Swing sorunu sadece vurgulayan biri gibi görünüyor.

UI'ye yapılan çağrıların doğru şekilde senkronize edilmesini sağlamak olan, ara katmanı (kontrolörün kontrolörü?) Tasarlayabilirsiniz. API'nizin UI olmayan kısımlarını UI API perspektifinden nasıl tasarlayabileceğinizi tam olarak bilmek imkansızdır ve çoğu geliştirici, UI API'sine uygulanan herhangi bir iş parçacığı korumasının kısıtlayıcı olduğu veya ihtiyaçlarını karşılamadığı konusunda şikayette bulunacaktır. Gereksinimlerinize dayanarak bu sorunu nasıl çözmek istediğinize karar vermenize izin vermek daha iyi

Dikkate almanız gereken en büyük sorunlardan biri, bilinen girdilere dayanarak belirli bir olay sırasını haklı çıkarma yeteneğidir. Örneğin, kullanıcı pencereyi yeniden boyutlandırırsa, Olay Kuyruğu modeli belirli bir olay sırasının gerçekleşeceğini garanti eder, bu basit görünebilir, ancak sıra diğer iş parçacıkları tarafından tetiklenen olaylara izin verdiyse, sipariş sırasını artık garanti edemezsiniz. hangi olayların meydana gelebileceği (bir yarış koşulu) ve birdenbire farklı durumlar için endişelenmeye başlamalısınız ve başka bir şey olana kadar bir şey yapmamanız ve etrafta devlet bayraklarını paylaşmanız ve spagetti ile son bulmanız gerekiyor.

Tamam, olayları yayınlandıkları zamana göre sipariş eden bir tür kuyruğa alarak çözebilirsiniz, ama zaten sahip olduğumuz şey bu değil mi? Ayrıca, B iş parçacığının A konusunu SONRA olayları üreteceğini garanti edemezsiniz.

İnsanların kodlarını düşünmek zorunda kalmaları için üzülmesinin temel nedeni, kodlarını / tasarımlarını düşünmeleridir. "Neden daha kolay olamaz?" Daha basit olamaz çünkü basit bir problem değil.

PS3'ün ne zaman piyasaya sürüldüğünü ve Sony'nin Cell işlemcisiyle konuştuğunu ve ayrı mantık satırları gerçekleştirme, ses kodunu çözme, video, yükleme ve model verilerini çözme yeteneğini hatırlıyorum. Bir oyun geliştiricisi, “Hepsi harika ama akışları nasıl senkronize ediyorsun?” Diye sordu.

Geliştiricinin bahsettiği sorun, bir noktada, tüm bu ayrı akışların çıktı için tek bir boruya senkronize edilmesi gerektiğiydi. Fakir sunum yapan kişi, aşina oldukları bir konu olmadığı için omuz silkti. Açıkçası, şimdi bu sorunu çözmek için çözümleri vardı, ama o zamanlar komik.

Modern bilgisayarlar aynı anda birçok farklı yerden çok fazla girdi alıyorlar, tüm bu girdilerin işlenmesi ve kullanıcıya uzaktan verilmesi gerekiyor, bu da diğer bilgilerin sunumunu engellemiyor, bu yüzden karmaşık bir sorun Tek bir basit çözüm.

Şimdi, çerçeveler arasında geçiş yapma yeteneğine sahip olmak, bu tasarım için kolay bir şey değil, ANCAK, bir anlığına MVC'yi alın, MVC çok katmanlı olabilir, yani, doğrudan UI çerçevesini yönetme ile ilgilenen bir MVC'ye sahip olabilirsiniz. daha sonra, diğer (potansiyel olarak çok dişli) çerçevelerle etkileşimlerle ilgilenen daha yüksek bir MVC katmanına sarılabilir, daha düşük MVC katmanının nasıl bildirileceğini / güncellendiğini belirlemek bu katmanın sorumluluğundadır.

Daha sonra bu farklı katmanları oluşturmak için tasarım desenleri ve fabrika veya üretici desenleri arayüzünü bağlamak için kodlamayı kullanırsınız. Bu, çok iş parçacıklı çerçevelerin, bir fikir olarak, bir orta katmanın kullanılmasıyla UI katmanından ayrıştırıldığı anlamına gelir.


2
Web ile bu sorunu yaşamazsınız. JavaScript'in kasten iş parçacığı desteği yoktur - bir JavaScript çalışma zamanı etkili bir şekilde yalnızca bir büyük olay kuyruğudur. (Evet, kesinlikle konuşan JS'nin WebWorker'ları olduğunu biliyorum - bunlar sinir sıkıntısı çekiyor ve diğer dillerdeki aktörler gibi davranıyorlar).
James_pic

1
@James_pic Aslında sahip olduğunuz tarayıcının olay kuyruğu için senkronize edici rol oynadığı, temelde bahsettiğimiz şey, arayan kişi güncellemelerin araç takımları olay kuyruğu sırasında gerçekleşmesini sağlamaktan sorumluydu
MadProgrammer

Evet kesinlikle. Web bağlamındaki temel fark, bu senkronizasyonun arayanın kodundan bağımsız olarak gerçekleşmesidir, çünkü çalışma zamanı, olay sırasının dışında kod yürütülmesine izin verecek bir mekanizma sağlamamaktadır. Bu yüzden arayan kişinin bunun sorumluluğunu almasına gerek yok. Bunun da NodeJS'nin geliştirilmesinin ardındaki motivasyonun büyük bir parçası olduğuna inanıyorum - eğer çalışma ortamı bir olay döngüsü ise, tüm kod varsayılan olarak olay döngüsünün farkındadır.
James_pic

1
Bununla birlikte, olay döngüsünde kendi olay döngüsüne sahip tarayıcı UI çerçeveleri var (Size Angular'a bakıyorum). Bu çerçeveler artık çalışma zamanı tarafından korunmadığından, arayanlar aynı zamanda diğer çerçevelerdeki çok iş parçacıklı kodda olduğu gibi kodun olay döngüsü içinde yürütülmesini sağlamalıdır.
James_pic

"Yalnızca ekran" kontrollerinin otomatik olarak iplik güvenliği sağlaması ile ilgili herhangi bir sorun olur mu? Birinin bir iş parçacığı tarafından yazılmış ve bir başkası tarafından okunan düzenlenebilir bir metin kontrolü varsa, bu işlemlerden en az birini kontrolün asıl kullanıcı durumuna senkronize etmeden yazının iş parçacığında görünmesini sağlamanın iyi bir yolu yoktur, ancak bu tür sorunlar yalnızca ekran denetimleri için önemli midir? Bunların, üzerinde yapılan işlemler için gerekli herhangi bir kilitleme veya kilitlemeyi kolayca sağlayabileceklerini ve arayanın ne zaman zamanlamalarını görmezden gelmelerine izin verebileceklerini
düşünürdüm
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.