2012-06-14 15 views
6

Özellikle pthreads durumunda nasıl uygulanırlar. Ne pthread senkronizasyon API'leri kaputun altında kullanıyorlar? Biraz sözde kod takdir edilecektir.Pthread'de okuma/yazma kilitleri nasıl uygulanır?

+7

belki de codez okuyabilirsiniz? – tbert

+1

Belki de bu [http: //www.cognitus.net/html/howto/pthreadSemiFAQ_10.html) yardımcı olabilir? –

cevap

8

Bir süredir pthreads programlama işlemlerini tamamlamamışım, ancak yaptığımda POSIX okuma/yazma kilitlerini hiç kullanmamıştım. Sorun şu ki çoğu zaman bir muteks yeterli olacaktır: yani. kritik bölümünüz küçüktür ve bölge, çift bariyerin endişelenmeye değer olduğu kadar kritik bir performans değildir.

Performansın söz konusu olduğu durumlarda, normalde atom işlemlerini (genellikle derleyici uzantısı olarak kullanılabilir) kullanmak daha iyi bir seçenektir (örn., Ek bölümün boyutu kritik bölümün boyutu değil, ek engeldir).

Tüm bu durumları ortadan kaldırdığınız zaman, gerçek bir rw-lock gerektiren belirli bir performans/adalet/rw-bias gereksiniminizin olduğu durumlarda kaldınız; ve POSIX rw-lock'un tüm ilgili performans/adalet parametrelerinin tanımsız ve uygulamaya özgü olduğunu keşfettiğinizde. Bu noktada, genellikle kendi hakkınızı uygulamaktan daha iyi olursunuz, böylece uygun adalet/rw-bias gereksinimlerinin karşılanmasını sağlayabilirsiniz.

Temel algoritma, kritik bölümde her birinin kaçının olduğunu ve bir iş parçacığının erişime izin verilmemesi durumunda, beklemek için uygun bir sıraya takmaktır. Çabalarınızın çoğu, iki sıraya hizmet etmek arasındaki uygun adalet/önyargıyı uygulamak olacaktır.

Aşağıdaki C benzeri pthreads benzeri sözde kod, söylemeye çalıştığım şeyi gösterir.

struct rwlock { 
    mutex admin; // used to serialize access to other admin fields, NOT the critical section. 
    int count; // threads in critical section +ve for readers, -ve for writers. 
    fifoDequeue dequeue; // acts like a cond_var with fifo behaviour and both append and prepend operations. 
    void *data; // represents the data covered by the critical section. 
} 

void read(struct rwlock *rw, void (*readAction)(void *)) { 
    lock(rw->admin); 
    if (rw->count < 0) { 
    append(rw->dequeue, rw->admin); 
    } 
    while (rw->count < 0) { 
    prepend(rw->dequeue, rw->admin); // Used to avoid starvation. 
    } 
    rw->count++; 
    // Wake the new head of the dequeue, which may be a reader. 
    // If it is a writer it will put itself back on the head of the queue and wait for us to exit. 
    signal(rw->dequeue); 
    unlock(rw->admin); 

    readAction(rw->data); 

    lock(rw->admin); 
    rw->count--; 
    signal(rw->dequeue); // Wake the new head of the dequeue, which is probably a writer. 
    unlock(rw->admin); 
} 

void write(struct rwlock *rw, void *(*writeAction)(void *)) { 
    lock(rw->admin); 
    if (rw->count != 0) { 
    append(rw->dequeue, rw->admin); 
    } 
    while (rw->count != 0) { 
    prepend(rw->dequeue, rw->admin); 
    } 
    rw->count--; 
    // As we only allow one writer in at a time, we don't bother signaling here. 
    unlock(rw->admin); 

    // NOTE: This is the critical section, but it is not covered by the mutex! 
    //  The critical section is rather, covered by the rw-lock itself. 
    rw->data = writeAction(rw->data); 

    lock(rw->admin); 
    rw->count++; 
    signal(rw->dequeue); 
    unlock(rw->admin); 
} 

Yukarıdaki kod gibi bir şey, herhangi bir kilitlenme uygulaması için bir başlangıç ​​noktasıdır. Probleminizin doğasına biraz düşünün ve dequeue'yi, hangi iş parçacığının bir sonraki adımda uyandırılacağını belirleyen uygun mantıkla değiştirin. Uygulamaya bağlı olarak, sınırlı sayıda/süreli okuyucunun leapfrog yazarlarına veya vize olmasına izin vermek yaygındır.

Elbette genel tercihim rw-kilitleri tamamen önlemek; genellikle atomik operasyonlar, muteksler, STM, mesaj iletimi ve kalıcı veri yapılarının bazı kombinasyonları kullanılarak. Bununla birlikte, gerçekten ihtiyacınız olanın bir rw-kilidi olduğu zamanlar vardır ve bunu yaptığınızda, onların nasıl çalıştığını bilmek faydalı olur, bu yüzden bu yardımcı oldu umarım.

DÜZENLEME - Yukarıda sözde kod beklemek yok (makul) sorusuna yanıt olarak: böylece bir yere içinde append(dequeue, mutex) veya prepend(dequeue, mutex) orada, dequeue uygulama bekleyin içerdiğini varsaydım

Ben kuyruk faaliyetleri ile ilgili Muteksleri geçirilen neden oldu

while(!readyToLeaveQueue()) { 
    wait(dequeue->cond_var, mutex); 
} 

: çizgisinde kod bloğu bulunmaktadır.

+0

RW kilitlerinin niçin ve nasıl engelleneceğine dair daha ayrıntılı bir açıklama getirebilir misiniz? Olmamam gerektiğini hissettiğim yerlerde ve ne hakkında konuştuğunuz hakkında daha fazla bilgi edinmek istiyorum. – puffadder

+0

RW kilitlerinden kaçınmakla ilgili değil. Cevabımda söylediğim gibi, bir RW ​​kilidine ihtiyacınız olduğunda bir tane kullanmalısınız. Sorun, bir RW ​​kilidine ihtiyaç duyulması ve aynı zamanda önyargı/adalet gerekliliklerine sahip olmanın nadir olması. Posix RW kilitleri hiçbir önyargı/adillik garantisi vermez ve bu nedenle kullanmak isteyebileceğiniz zamanlar için portatif olarak portatif değildir. Kendi taşınabilir RW ​​kilidini uygulamak zor olmadığı için, siz de olabilirsiniz. – Recurse

+0

Koddaki sinyalleri nerede bekliyorsunuz? Birkaç yerde sinyal (rw-> deque) görüyorum ama bu sinyalde beklemek için kod yok. – pythonic

0

Her bir uygulama farklı olabilir, ancak normalde POSIX gereksinimi nedeniyle bir iş parçacığının birden çok kez bir rwlock üzerinde okuma kilidini elde edebilmesi nedeniyle okuyucular varsayılan olarak desteklenmelidir. Yazarlar tercih ettiyse, bir yazar ne zaman beklerse, okuyucu okuyucunun bir okuma kilidine sahip olduğunu belirleyemediyse, okuyucu ikinci okuma-kilitleme girişimi üzerinde kilitlenecekti, ancak bunu belirlemenin tek yolu tüm ileti dizilerinin bir listesini saklamaktı. Bu, zaman ve mekan gereksinimlerinde çok verimsiz olan okuma kilitlerini tutar.

+1

Yorumunuz sadece reentrant rw-lock için geçerlidir. Cevabımda da görüldüğü gibi, reentran olmayan kilitler sadece bir sayım gerektirir. Ayrıca, bir liste reentrancy uygulamak için çok zayıf bir veri yapısı seçenektir. Kısa diziler, bitmapler ve lineer karmaların önbellek-bilinçli kombinasyonu, defter tutma işlemini çok daha fazla alan/zaman verimli hale getirebilir. – Recurse

+0

Reentrant ile tekrar etmek istediniz mi? Reentrancy, sadece tekrarlayan kilitlemeden çok daha güçlü bir gerekliliktir. –