Threadafe ne anlama geliyor?


124

Son zamanlarda bir iş parçacığından (UI iş parçacığı dışında) bir metin kutusuna erişmeyi denedim ve bir istisna atıldı. "Kodun iş parçacığı açısından güvenli olmadığı" hakkında bir şeyler söyledi ve ben de bir temsilci yazdım (MSDN'den örnek yardımcı oldu) ve onun yerine onu çağırdım.

Ama yine de tüm ekstra kodun neden gerekli olduğunu tam olarak anlamadım.

Güncelleme: Kontrol edersem ciddi sorunlarla karşılaşır mıyım?

Controls.CheckForIllegalCrossThread..blah =true

5
Tipik olarak, "iş parçacığı güvenli" terimi kullanan kişinin, en azından o kişi için ne anlama geldiğini düşündüğü anlamına gelir. Bu nedenle, çok kullanışlı bir dil yapısı değildir - iş parçacıklı kodun davranışı hakkında konuşurken çok, çok daha spesifik olmanız gerekir.


@dave Üzgünüm aramayı denedim, ama vazgeçtim ... yine de teşekkürler ..
Vivek Bernard

1
ortaya Race-Condition
çıkmayan

Yanıtlar:


121

Eric Lippert'in "iş parçacığı güvenli" dediğiniz bu şey nedir başlıklı güzel bir blog yazısı var ? Wikipedia'da bulunan iş parçacığı güvenliğinin tanımı hakkında.

Bağlantılardan çıkarılan 3 önemli şey:

"Bir kod parçası, birden çok iş parçacığı tarafından eşzamanlı yürütme sırasında doğru şekilde çalışıyorsa iş parçacığı açısından güvenlidir."

"Özellikle, aynı paylaşılan verilere erişmek için birden fazla iş parçacığı ihtiyacını karşılamalı, ..."

"… Ve herhangi bir zamanda yalnızca bir iş parçacığı tarafından erişilen paylaşılan bir veri parçasına duyulan ihtiyaç."

Kesinlikle okumaya değer!


25
Gelecekte herhangi bir zamanda kötüye gidebileceğinden lütfen yalnızca yanıtlardan kaçının.
akhil_mittal


106

En basit ifadeyle, iş parçacığı güvenli, birden çok iş parçacığından erişmenin güvenli olduğu anlamına gelir. Bir programda birden fazla iş parçacığı kullandığınızda ve bunların her biri bellekteki ortak bir veri yapısına veya konuma erişmeye çalıştığında, birkaç kötü şey olabilir. Yani, bu kötü şeyleri önlemek için fazladan bir kod eklersiniz. Örneğin, iki kişi aynı belgeyi aynı anda yazıyorsa, kaydedilecek ikinci kişi ilk kişinin çalışmasının üzerine yazacaktır. İş parçacığını güvenli hale getirmek için, 2. kişinin belgeyi düzenlemesine izin vermeden önce 2. kişiyi 1. kişinin görevini tamamlamasını beklemeye zorlamalısınız.


11
Buna senkronizasyon denir. Sağ?
JavaTechnical

3
Evet. Çeşitli evreleri paylaşılan bir kaynağa erişim için beklemeye zorlamak, senkronizasyon ile gerçekleştirilebilir.
Vincent Ramdhanie

Gregory'nin kabul ettiği cevabından, "" Birden fazla iş parçacığı tarafından eşzamanlı yürütme sırasında doğru şekilde çalışıyorsa, bir kod parçası iş parçacığı açısından güvenlidir "diyor. "İş parçacığını güvenli hale getirmek için, 1. kişiyi beklemeye zorlamalısın" derken, siz öyle değil derken eşzamanlı kabul edilebilir mi diyor? Açıklayabilir misiniz?
Honey

Aynı şey. Kod iş parçacığını güvenli yapan şeyin bir örneği olarak basit bir mekanizma öneriyorum. kullanılan mekanizma ne olursa olsun, aynı kodu çalıştıran birden fazla iş parçacığı birbirini engellememelidir.
Vincent Ramdhanie

Öyleyse bu yalnızca global ve statik değişkenleri kullanan kod için geçerli mi? Belgeleri düzenleyen kişi örneğinizi kullanarak, 2. kişinin belge yazma kodunu başka bir belgede çalıştırmasını engellemenin mantıklı olmadığını düşünüyorum.
Aaron Franke

18

Wikipedia'nın İş Parçacığı Güvenliği hakkında bir makalesi var.

Bu tanımlar sayfası (bir reklamı atlamanız gerekir - üzgünüm) onu şu şekilde tanımlar:

Bilgisayar programlamasında, iş parçacığı güvenli, iş parçacıkları arasında istenmeyen etkileşim olmaksızın birden çok programlama iş parçacığından çağrılabilen bir program bölümünü veya yordamı tanımlar.

Bir iş parçacığı, bir programın yürütme yoludur. Tek iş parçacıklı bir program yalnızca bir iş parçacığına sahip olacaktır ve bu nedenle bu sorun ortaya çıkmaz. Hemen hemen tüm GUI programları birden çok yürütme yoluna ve dolayısıyla iş parçacığına sahiptir - biri GUI'nin görüntüsünü işlemek ve kullanıcı girişini vermek için ve en az biri de programın işlemlerini fiilen gerçekleştirmek için olmak üzere en az iki tane vardır.

Bu, program çalışırken herhangi bir uzun süre çalışan işlemi UI olmayan iş parçacıklarına devrederek kullanıcı arabiriminin hala yanıt verebilmesi için yapılır. Bu iş parçacıkları bir kez oluşturulabilir ve programın ömrü boyunca var olabilir veya yalnızca gerektiğinde oluşturulabilir ve bittiğinde yok edilebilir.

Bu iş parçacıklarının genellikle ortak eylemleri gerçekleştirmesi gerekeceğinden - disk i / o, sonuçları ekrana çıkarma vb. - kodun bu bölümlerinin, birden çok iş parçacığından çağrılmayı işleyebilecek şekilde yazılması gerekecektir. Aynı zaman. Bu, aşağıdaki gibi şeyleri içerecektir:

  • Veri kopyaları üzerinde çalışmak
  • Kritik kodun etrafına kilitler ekleme

8

Basitçe, iş parçacığı güvenli, bir yöntemin veya sınıf örneğinin aynı anda birden çok iş parçacığı tarafından herhangi bir sorun oluşmadan kullanılabileceği anlamına gelir.

Aşağıdaki yöntemi düşünün:

private int myInt = 0;
public int AddOne()
{
    int tmp = myInt;
    tmp = tmp + 1;
    myInt = tmp;
    return tmp;
}

Şimdi hem A hem de B iş parçacığı AddOne () çalıştırmak istiyor. ama önce A başlar ve myInt (0) değerini tmp'ye okur. Şimdi bazı nedenlerden dolayı zamanlayıcı A evresini durdurmaya ve B evresine yürütmeyi ertelemeye karar verir. B İş Parçacığı artık myInt (hala 0) değerini kendi değişken tmp'ye okur. B iş parçacığı tüm yöntemi bitirir, böylece sonunda myInt = 1. Ve 1 döndürülür. Şimdi İplik A'nın sırası yine. Konu A devam ediyor. Ve tmp'ye 1 ekler (tmp, iplik A için 0'dı). Ve sonra bu değeri myInt'e kaydeder. myInt yine 1.

Bu durumda, AddOne yöntemi iki kez çağrıldı, ancak yöntem iş parçacığı güvenli bir şekilde uygulanmadığından, beklendiği gibi myInt değeri 2 değil, 1, çünkü ikinci iş parçacığı ilk iş parçacığı tamamlanmadan önce myInt değişkenini okudu güncelleniyor.

İş parçacığı güvenli yöntemler oluşturmak, önemsiz olmayan durumlarda çok zordur. Ve epeyce teknik var. Java'da bir yöntemi senkronize edilmiş olarak işaretleyebilirsiniz, bu, belirli bir zamanda yalnızca bir iş parçacığının bu yöntemi çalıştırabileceği anlamına gelir. Diğer konular sırada bekler. Bu, bir yöntem iş parçacığını güvenli kılar, ancak bir yöntemde yapılacak çok iş varsa, bu çok fazla alan israfına neden olur. Başka bir teknik, 'bir yöntemin yalnızca küçük bir bölümünü senkronize olarak işaretlemektir'bir kilit veya semafor oluşturarak ve bu küçük parçayı kilitleyerek (genellikle kritik bölüm olarak adlandırılır). Kilitsiz iş parçacığı güvenli olarak uygulanan bazı yöntemler bile vardır; bu, birden fazla iş parçacığının aynı anda hiçbir soruna neden olmadan içlerinden geçebileceği şekilde oluşturuldukları anlamına gelir, bu yalnızca bir yöntem olduğunda söz konusu olabilir. bir atomik çağrı yürütür. Atomik çağrılar, kesintiye uğratılamayan ve aynı anda yalnızca bir iş parçacığı tarafından yapılabilen çağrılardır.


AddOne yöntemi iki kez çağrıldıysa
Sujith PS

7

Gerçek dünyada meslekten olmayan kişi için örnek

Diyelim ki internet ve mobil bankacılıkta bir banka hesabınız var ve hesabınızda sadece 10 dolar var. Mobil bankacılığı kullanarak başka bir hesaba bakiye transferi yaptınız ve bu arada aynı banka hesabını kullanarak çevrimiçi alışveriş yaptınız. Bu banka hesabı iş parçacığı güvenli değilse, banka aynı anda iki işlem yapmanıza izin verir ve ardından banka iflas eder.

İş parçacığı güvenli, aynı anda birden fazla iş parçacığı nesneye erişmeye çalışırsa bir nesnenin durumunun değişmeyeceği anlamına gelir.


5

"Pratikte Java Eş Zamanlılığı" kitabından daha fazla açıklama alabilirsiniz:

Bir sınıf, birden çok iş parçacığından erişildiğinde doğru şekilde davranırsa, bu iş parçacıklarının çalışma zamanı ortamı tarafından yürütülmesinin zamanlamasına veya araya eklenmesine bakılmaksızın ve çağıran kod kısmında ek senkronizasyon veya başka bir koordinasyon olmadan iş parçacığı açısından güvenlidir.


4

Bir modül, çok iş parçacıklı ve eşzamanlı kullanım karşısında değişmezlerini koruyabileceğini garanti ediyorsa, iş parçacığı açısından güvenlidir.

Burada bir modül, bir veri yapısı, sınıf, nesne, yöntem / prosedür veya işlev olabilir. Temel olarak kapsamlı kod parçası ve ilgili veriler.

Garanti, potansiyel olarak belirli bir CPU mimarisi gibi belirli ortamlarla sınırlı olabilir, ancak bu ortamlar için geçerli olmalıdır. Ortamların açık bir şekilde sınırlandırılması yoksa, genellikle kodun derlenebileceği ve yürütülebileceği tüm ortamlar için geçerli olduğu anlamına gelir.

İş parçacığı güvensiz modüller , çok iş parçacıklı ve eşzamanlı kullanım altında doğru şekilde çalışabilir, ancak bu, dikkatli tasarımdan ziyade genellikle şans ve tesadüflere bağlıdır. Bazı modüller sizin için kırılmasa bile, başka ortamlara taşındığında kırılabilir.

Çoklu iş parçacığı hatalarının ayıklanması genellikle zordur. Bazıları yalnızca ara sıra olurken, diğerleri agresif bir şekilde tezahür eder - bu da ortama özgü olabilir. İnce yanlış sonuçlar veya çıkmazlar olarak tezahür edebilirler. Veri yapılarını öngörülemeyen şekillerde bozabilir ve görünüşte imkansız olan diğer hataların kodun diğer uzak kısımlarında görünmesine neden olabilirler. Çok uygulamaya özel olabilir, bu nedenle genel bir tanım vermek zordur.


3

İş parçacığı güvenliği : İş parçacığı güvenli bir program, verilerini bellek tutarlılık hatalarından korur. Oldukça çok iş parçacıklı bir programda, iş parçacığı güvenli bir program, aynı nesneler üzerindeki birden çok iş parçacığından çoklu okuma / yazma işlemlerinde herhangi bir yan etkiye neden olmaz. Farklı iş parçacıkları, tutarlılık hataları olmadan nesne verilerini paylaşabilir ve değiştirebilir.

Gelişmiş eşzamanlılık API'sini kullanarak iş parçacığı güvenliğini sağlayabilirsiniz. Bu belge sayfası , iş parçacığı güvenliğini sağlamak için iyi programlama yapıları sağlar.

Lock Objects birçok eşzamanlı uygulamayı basitleştiren kilitleme deyimlerini destekler.

Yürütücüler , iş parçacıklarını başlatmak ve yönetmek için yüksek düzeyde bir API tanımlar. Java.util.concurrent tarafından sağlanan yürütme uygulamaları, büyük ölçekli uygulamalar için uygun iş parçacığı havuzu yönetimi sağlar.

Eşzamanlı Koleksiyonlar , büyük veri koleksiyonlarının yönetilmesini kolaylaştırır ve senkronizasyon ihtiyacını büyük ölçüde azaltabilir.

Atomik Değişkenler , senkronizasyonu en aza indiren ve bellek tutarlılığı hatalarını önlemeye yardımcı olan özelliklere sahiptir.

ThreadLocalRandom (JDK 7'de), birden çok iş parçacığından etkin sözde rasgele sayıların üretilmesini sağlar.

Bakın java.util.concurrent ve java.util.concurrent.atomic diğer programlama yapıları için de paketler.


1

Açıkça bir WinForms ortamında çalışıyorsunuz. WinForms denetimleri, iş parçacığı benzeşimi sergiler; bu, oluşturuldukları iş parçacığının onlara erişmek ve bunları güncellemek için kullanılabilen tek iş parçacığı olduğu anlamına gelir. Bu nedenle, MSDN'de ve başka yerlerde aramayı ana iş parçacığına nasıl geri sıralayacağınızı gösteren örnekler bulacaksınız.

Normal WinForms uygulaması, tüm kullanıcı arabirimi çalışmalarınıza ayrılmış tek bir iş parçacığına sahip olmaktır.


1

Ben kavramı bulmak http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 Genellikle bir yöntem vardır ve böyle bir küresel değişken olarak bir yan etkisine dayanır zaman olduğu kadar güvensiz parçacığı ne düşündüğünü olmak.

Örneğin, kayan nokta sayılarını dizeye biçimlendiren bir kod gördüm, bunlardan ikisi farklı evrelerde çalıştırılırsa, decimalSeparator'un genel değeri kalıcı olarak '.' Olarak değiştirilebilir.

//built in global set to locale specific value (here a comma)
decimalSeparator = ','

function FormatDot(value : real):
    //save the current decimal character
    temp = decimalSeparator

    //set the global value to be 
    decimalSeparator = '.'

    //format() uses decimalSeparator behind the scenes
    result = format(value)

    //Put the original value back
    decimalSeparator = temp

-2

İş parçacığı güvenliğini anlamak için aşağıdaki bölümleri okuyun :

4.3.1. Örnek: Delegasyonu Kullanan Araç İzleyici

Yetkilendirmenin daha önemli bir örneği olarak, iş parçacığı korumalı bir sınıfa delege eden araç izleyicisinin bir sürümünü oluşturalım. Konumları bir Haritada saklıyoruz, bu nedenle iş parçacığı açısından güvenli bir Harita uygulamasıyla başlıyoruz ConcurrentHashMap. Konumu MutablePoint, Liste 4.6'da gösterilen yerine değişmez bir Nokta sınıfı kullanarak da saklıyoruz .

4.6. DelegatingVehicleTracker tarafından kullanılan Immutable Point sınıfı.

 class Point{
  public final int x, y;

  public Point() {
        this.x=0; this.y=0;
    }

  public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

}

Pointdeğişmez olduğu için iş parçacığı açısından güvenlidir. Değişmez değerler özgürce paylaşılabilir ve yayınlanabilir, bu nedenle artık konumları iade ederken kopyalamamıza gerek kalmaz.

DelegatingVehicleTrackerListe 4.7'de herhangi bir açık senkronizasyon kullanılmaz; duruma tüm erişim, tarafından yönetilir ConcurrentHashMapve Haritanın tüm anahtarları ve değerleri değişmezdir.

4.7 listesi. Bir ConcurrentHashMap'e İş Parçacığı Güvenliği Temsilcisi Veriliyor.

  public class DelegatingVehicleTracker {

  private final ConcurrentMap<String, Point> locations;
    private final Map<String, Point> unmodifiableMap;

  public DelegatingVehicleTracker(Map<String, Point> points) {
        this.locations = new ConcurrentHashMap<String, Point>(points);
        this.unmodifiableMap = Collections.unmodifiableMap(locations);
    }

  public Map<String, Point> getLocations(){
        return this.unmodifiableMap; // User cannot update point(x,y) as Point is immutable
    }

  public Point getLocation(String id) {
        return locations.get(id);
    }

  public void setLocation(String id, int x, int y) {
        if(locations.replace(id, new Point(x, y)) == null) {
             throw new IllegalArgumentException("invalid vehicle name: " + id);
        }
    }

}

MutablePointPoint yerine orijinal sınıfı getLocationskullansaydık, iş parçacığı için güvenli olmayan değiştirilebilir duruma bir referans yayınlayarak kapsüllemeyi bozuyor olurduk . Araç izleme sınıfının davranışını biraz değiştirdiğimize dikkat edin; monitör sürümü konumların anlık görüntüsünü döndürürken, yetkilendirme sürümü araç konumlarının değiştirilemez ancak "canlı" bir görünümünü döndürür. Bu, eğer A evresi çağırırsa getLocationsve B iş parçacığı daha sonra bazı noktaların konumunu değiştirirse, bu değişiklikler A evresine döndürülen Haritaya yansıtılır.

4.3.2. Bağımsız Durum Değişkenleri

Temel durum değişkenleri bağımsız olduğu sürece, iş parçacığı güvenliğini birden fazla temel durum değişkenine devredebiliriz, bu da bileşik sınıfın birden çok durum değişkenini içeren herhangi bir değişmezi empoze etmediği anlamına gelir.

VisualComponentListing 4.9, istemcilerin fare ve tuş vuruşu olayları için dinleyicileri kaydetmesine olanak tanıyan bir grafik bileşendir. Her türden kayıtlı dinleyicilerin bir listesini tutar, böylece bir olay meydana geldiğinde uygun dinleyiciler çağrılabilir. Ancak fare dinleyicileri ve anahtar dinleyiciler arasında hiçbir ilişki yoktur; ikisi bağımsızdır ve bu nedenle VisualComponentiş parçacığı güvenlik yükümlülüklerini temeldeki iki iş parçacığı güvenli listeye devredebilir.

4.9 listeleniyor. İş Parçacığı Güvenliğini Birden Çok Temel Durum Değişkenine Delege Etme.

public class VisualComponent {
    private final List<KeyListener> keyListeners 
                                        = new CopyOnWriteArrayList<KeyListener>();
    private final List<MouseListener> mouseListeners 
                                        = new CopyOnWriteArrayList<MouseListener>();

  public void addKeyListener(KeyListener listener) {
        keyListeners.add(listener);
    }

  public void addMouseListener(MouseListener listener) {
        mouseListeners.add(listener);
    }

  public void removeKeyListener(KeyListener listener) {
        keyListeners.remove(listener);
    }

  public void removeMouseListener(MouseListener listener) {
        mouseListeners.remove(listener);
    }

}

VisualComponentCopyOnWriteArrayListher dinleyici listesini saklamak için a kullanır ; bu, özellikle dinleyici listelerini yönetmek için uygun olan iş parçacığı güvenli bir Liste uygulamasıdır (bkz. Bölüm 5.2.3). Her Liste iş parçacığı açısından güvenlidir ve birinin durumunu diğerinin durumuna bağlayan herhangi bir kısıtlama olmadığından VisualComponent, iş parçacığı güvenlik sorumluluklarını temeldeki mouseListenersve keyListenersnesnelere devredebilir .

4.3.3. Yetkilendirme Başarısız Olduğunda

Çoğu bileşik sınıf, VisualComponentbileşen durum değişkenlerini ilişkilendiren değişmezlere sahiptirler. NumberRangeListing 4.10 AtomicIntegers, durumunu yönetmek için iki kullanır , ancak ek bir kısıtlama getirir - ilk sayının ikinciden küçük veya ona eşit olması.

4.10 listesi. Değişkenlerini Yeterince Korumayan Sayı Aralığı Sınıfı. Bunu yapma.

public class NumberRange {

  // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

  public void setLower(int i) {
        //Warning - unsafe check-then-act
        if(i > upper.get()) {
            throw new IllegalArgumentException(
                    "Can't set lower to " + i + " > upper ");
        }
        lower.set(i);
    }

  public void setUpper(int i) {
        //Warning - unsafe check-then-act
        if(i < lower.get()) {
            throw new IllegalArgumentException(
                    "Can't set upper to " + i + " < lower ");
        }
        upper.set(i);
    }

  public boolean isInRange(int i){
        return (i >= lower.get() && i <= upper.get());
    }

}

NumberRangeolan iş parçacığı güvenli değil ; alt ve üst sınırlayan değişmezi korumaz. setLowerVe setUpperyöntemler bu değişmeyen uygulamaya çalışırız ama çok kötü yapmak. Hem setLowerve setUppercheck-sonra perdelik dizileri, ancak bunları atomik yapmak için yeterli kilitleme kullanmayın. Sayı aralığı (0, 10) tutarsa ​​ve bir iş parçacığı çağırırken bir setLower(5)başka iş parçacığı çağırırsa setUpper(4), bazı şanssız zamanlamalarla her ikisi de ayarlayıcılardaki kontrolleri geçecek ve her iki değişiklik de uygulanacaktır. Sonuç, aralığın artık (5, 4) değerini tutmasıdır - geçersiz bir durum . Dolayısıyla , temeldeki AtomicIntegers iş parçacığı açısından güvenliyken, bileşik sınıf değildir . Çünkü temeldeki durum değişkenleri lowerveupperbağımsız NumberRangedeğildir, basitçe iş parçacığı güvenliğini iş parçacığı güvenli durum değişkenlerine devredemez.

NumberRangeortak bir kilitle alt ve üst koruma gibi değişmezlerini korumak için kilit kullanılarak vida güvenli hale getirilebilir. Müşterilerin değişmezlerini altüst etmesini önlemek için alt ve üst yayınlamaktan da kaçınmalıdır.

Bir sınıfta olduğu gibi bileşik eylemler varsa NumberRange, yetkilendirme tek başına iş parçacığı güvenliği için uygun bir yaklaşım değildir. Bu durumlarda, bileşik eylemin tamamı temel durum değişkenlerine devredilemedikçe, bileşik eylemlerin atomik olmasını sağlamak için sınıf kendi kilitlemesini sağlamalıdır.

Bir sınıf, birden çok bağımsız iş parçacığı güvenli durum değişkeninden oluşuyorsa ve geçersiz durum geçişlerine sahip hiçbir işlem içermiyorsa, temel durum değişkenlerine iş parçacığı güvenliğini delege edebilir.

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.