2010-04-29 11 views
5

Başka bir sorudaki bazı tartışmalar, çok parçalı Python programlarında kilitlemenin gerekli olduğu durumları daha iyi anlamam için beni teşvik etti. Python'da iş parçacığı üzerine this10 numaralı makalede, birden çok iş parçacığı paylaşılan durumu olduğunda ortaya çıkabilen birkaç sağlam, test edilebilir tuzak örneğine sahibim. Bu sayfada verilen örnek yarış durumu, sözlükte saklanan paylaşılan bir değişkeni okuyan ve işleyen iş parçacıkları arasındaki yarışları içerir. Bence burada bir yarış için durum çok açık ve neyse ki çok iyi bir şekilde test edilebilir. Bununla birlikte, liste ekleri veya değişken artışlar gibi atom işlemleriyle bir yarış koşulunu uyandırmayı başaramadım. Ben başarısızlık (100x 100k okuyuculu ekler) olmadan yukarıda testi varPython iş parçacığının paylaşılan durumu güvenli bir şekilde işleyebildiği bazı durumlar var mı?

from threading import Thread, Lock 
import operator 

def contains_all_ints(l, n): 
    l.sort() 
    for i in xrange(0, n): 
     if l[i] != i: 
      return False 
    return True 

def test(ntests): 
    results = [] 
    threads = [] 
    def lockless_append(i): 
     results.append(i) 
    for i in xrange(0, ntests): 
     threads.append(Thread(target=lockless_append, args=(i,))) 
     threads[i].start() 
    for i in xrange(0, ntests): 
     threads[i].join() 
    if len(results) != ntests or not contains_all_ints(results, ntests): 
     return False 
    else: 
     return True 

for i in range(0,100): 
    if test(100000): 
     print "OK", i 
    else: 
     print "appending to a list without locks *is* unsafe" 
     exit() 

: Bu test etraflıca böyle bir yarış göstermeye çalışır. Kimseyi başarısızlığa uğratabilir mi? İş parçacığı tarafından atomik, artımlı, modifikasyon yoluyla yanlış davranabilecek bir başka nesne sınıfı var mıdır?

Bu 'atomik' anlambilimsel sözler Python'daki diğer işlemler için geçerli midir? Bu doğrudan GIL ile ilgili mi?

+0

Eşzamanlı uygulamalarda test doğruluğu kanıtlamak için geçerli bir yöntem değildir. Sorunu asla tetiklemeyecek, beklenmedik şekilde tahmin edilebilir bir şekilde yürütmeye zorlamak için herhangi bir özel testin yapılması çok kolaydır, ancak koddaki en küçük değişiklik (ör.bunu gerçek bir dünya durumuna getirirken) kusuru anında gösterebilir. Eşzamanlı yazılımın doğruluğu kanıtlanmalı, test edilmemelidir. – Kylotan

cevap

7

Bir listeye ekleme iş parçacığı için güvenli, evet. Sadece GIL tutarken bir listeye ekleyebilir ve liste, append işlemi sırasında GIL'i serbest bırakmamaya özen gösterir (sonuçta, oldukça basit bir işlemdir.) siparişi farklı iş parçacığı ekleme işlemlerinin gerçekleştirildiği Tabii ki, kapmak için elbette ki, ama hepsi bir seri boyunca hiçbir zaman serbest bırakılmadığı için sıkı bir şekilde serileştirilmiş operasyonlar olacaklar.

Diğer işlemler için aynı şey geçerli değildir. Python'daki birçok işlem, rasgele Python kodunun yürütülmesine neden olabilir ve bu da GIL'in serbest bırakılmasına neden olabilir. Örneğin, i += 1 üç ayrı işlemdir, "i", "1'e ekle" ve "i içinde saklayın". "1 ekleyin" ifadesi bu durumda it.__iadd__(1)'a çevirebilir ve ne olursa olsun,

Python nesnelerinin kendileri kendi iç durumlarını korurlar - dicts, iki farklı iş parçacığı tarafından bozulmaz, ancak buradaki veriler içsel olarak tutarlı olmalıdır. dict ne de GiL. o az olasıdır ancak yine de mümkün şeyler düşündüğünden daha farklı sonuna kadar yaparak (her zamanki iplik moda) hariç, o korumak için her şeyi yapar CPython yılında

1

, iplik geçiş sys.getcheckinteval() kodları yürütüldüğünde yapılır. Bu nedenle, tek bir bayt kodu yürütülürken bir içerik anahtarı asla oluşmaz ve tek bir bayt kodu olarak kodlanmış işlemler, bayt kodu diğer Python kodunu çalıştırmazsa veya GIL'yi serbest bırakan C kodunu çağırmazsa, doğal olarak atomik ve iş parçacıklı değildir. Yerleşik toplama türleri (diksiyon, liste vb.) Üzerindeki çoğu işlem, "doğal olarak threadafe" kategorisine girer. Ancak bu, Python'un C uygulamasına özgü bir uygulama detayıdır ve buna güvenilmemelidir. Python'un diğer sürümleri (Jython, IronPython, PyPy vb.) Aynı şekilde davranmayabilir. Ayrıca, CPython'un gelecek sürümlerinin bu davranışı sürdüreceğine dair bir garanti de yoktur.

+0

Bu, neler olup bittiğine dair sezgilerimi izledi, ama diğer uygulamaları incelemeyi asla düşünmemiştim. Ben sadece CPython kullanıyorum. Bunu, alternatif uygulamalara karşı geleceğe uyumlu hale getirmek için, bu ayrıntıya güvenilmemelidir. –

+0

Bayt kodu ayrımları çoğunlukla çok ilginç değildir, çünkü neredeyse tüm bayt kodları daha fazla Python kodu çalıştırma potansiyeline sahiptir ve GIL'in kendisini serbest bırakma potansiyeli yoktur. "list.append()", örneğin, bir bytecode değildir ve gerçek "append" çalışması, "CALL_FUNCTION" opcode tarafından yürütülür; –