Condition_variable.notify_one () öğesini çağırmadan önce kilidi edinmem gerekir mi?


90

Kullanımı konusunda biraz kafam karıştı std::condition_variable. Aramadan önce bir unique_lockon a oluşturmam gerektiğini anlıyorum . Bulamadığım şey, aramadan önce ya da benzersiz bir kilit edinmem gerekip gerekmediğidir .mutexcondition_variable.wait()notify_one()notify_all()

Örnekleri cppreference.com çelişkilidir. Örneğin notify_one sayfası şu örneği verir:

#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>

std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;

void waits()
{
    std::unique_lock<std::mutex> lk(cv_m);
    std::cout << "Waiting... \n";
    cv.wait(lk, []{return i == 1;});
    std::cout << "...finished waiting. i == 1\n";
    done = true;
}

void signals()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Notifying...\n";
    cv.notify_one();

    std::unique_lock<std::mutex> lk(cv_m);
    i = 1;
    while (!done) {
        lk.unlock();
        std::this_thread::sleep_for(std::chrono::seconds(1));
        lk.lock();
        std::cerr << "Notifying again...\n";
        cv.notify_one();
    }
}

int main()
{
    std::thread t1(waits), t2(signals);
    t1.join(); t2.join();
}

Burada kilit birincisi notify_one()için değil, ikincisi için elde edilir notify_one(). Örnekler içeren diğer sayfalara baktığımda farklı şeyler görüyorum, çoğunlukla kilidi almıyorum.

  • Aramadan önce muteksi kilitlemeyi kendim seçebilir miyim notify_one()ve neden kilitlemeyi seçeyim?
  • Verilen örnekte, neden ilki için kilit yok notify_one(), ancak sonraki çağrılar için var. Bu örnek yanlış mı yoksa bir mantık var mı?

Yanıtlar:


77

Arama yaparken kilit tutmanıza gerek yoktur condition_variable::notify_one(), ancak yine de iyi tanımlanmış bir davranış olması ve bir hata olmaması anlamında yanlış değildir.

Bununla birlikte, bekleyen iş parçacığı çalıştırılabilir hale getirilirse (varsa), bildirim iş parçacığının tuttuğu kilidi elde etmeye çalışacağından, bu bir "pessimizasyon" olabilir. Bence notify_one()veya arama sırasında bir koşul değişkeniyle ilişkili kilidi tutmaktan kaçınmanın iyi bir kural olduğunu düşünüyorum notify_all(). Bkz. Pthread Mutex: pthread_mutex_unlock () , notify_one()gelişmiş performansın ölçülebilir şekilde pthread eşdeğerini çağırmadan önce bir kilidi serbest bıraktığı bir örnek için çok fazla zaman tüketir .

Döngüdeki lock()aramanın whilebir noktada gerekli olduğunu unutmayın , çünkü while (!done)döngü durumu kontrolü sırasında kilidin tutulması gerekir . Ancak arama için bekletilmesine gerek yoktur notify_one().


2016-02-27 : Bir yarış durumu olup olmadığına ilişkin yorumlarda bazı soruları ele alan büyük güncelleme, kilit notify_one()arama için yardımcı olmuyor . Ben soru neredeyse iki yıl önce istendi çünkü bu güncelleştirme geç olduğunu biliyorum, ama (yapımcı eğer olası bir yarış durumu hakkında Cookie sorusuna @ adrese istiyorum signals()bu örnekte) çağırır notify_one()(sadece tüketici önce waits()bu örnekte) arayabilir wait().

Anahtar olan şey i, tüketicinin yapacak "işi" olup olmadığını gerçekten gösteren nesnedir. Bu condition_variable, tüketicinin bir değişikliği verimli bir şekilde beklemesine izin veren bir mekanizmadır i.

Üreticinin güncelleme isırasında kilidi tutması gerekir ve tüketici kontrol ederken ive ararken condition_variable::wait()(beklemesi gerekiyorsa) kilidi tutmalıdır . Bu durumda kilit nokta , tüketici bu kontrol ve beklemeyi yaptığında kilidin tutulması (genellikle kritik bölüm olarak adlandırılır) olması gerektiğidir. Kritik bölüm üretici güncelleme iyaptığında ve tüketici kontrol edip beklediğinde tutulduğundan, tüketicinin ne zaman kontrol edeceği ve ne zaman arayacağı arasında geçiş iyapma imkanı yoktur . Bu, koşul değişkenlerinin doğru kullanımı için temel noktadır.iicondition_variable::wait()

C ++ standardı, condition_variable :: wait () ifadesinin bir yüklemle çağrıldığında aşağıdaki gibi davrandığını söyler (bu durumda olduğu gibi):

while (!pred())
    wait(lock);

Tüketici kontrol ettiğinde ortaya çıkabilecek iki durum vardır i:

  • eğer io zaman tüketici aramaları 0'dır cv.wait(), sonra iyine zaman 0 olacaktır wait(lock)uygulamanın parçası denir - bu kilitler teminat altın doğru kullanımı. Bu durumda, üreticinin, tüketici arayana kadar (ve çağrı, bir bildirimi uygun şekilde 'yakalamak' 'için yapması gereken her şeyi yapana kadar condition_variable::notify_one()) kendi whiledöngüsünde arama fırsatı bulamaz - bunu yapana kadar kilidi açmayacaktır. ). Yani bu durumda tüketici bildirimi kaçıramaz.cv.wait(lk, []{return i == 1;})wait()wait()

  • eğer itüketici çağrılar zaman zaten 1'dir cv.wait(), wait(lock)çünkü uygulamanın parçası olarak adlandırılan asla while (!pred())testi iç döngü sona erdirmek neden olacaktır. Bu durumda notify_one () çağrısının ne zaman gerçekleştiği önemli değildir - tüketici engellemeyecektir.

Örnek burada kullanmanın ek karmaşıklığı var donetüketici kabul ettiğini yapımcı iplik geri sinyal değişkeni i == 1, ama bu hiç de analiz değiştirir sanmıyorum çünkü erişimin tüm doneokuma ve değiştirme ikisi için ( aynı kritik bölümlerde içerdiğini iken) yapılır ive condition_variable.

Eğer işaret @ EH9 bu soruya bakarsak, Sync std :: atomik ve std :: condition_variable kullanarak güvenilmez , sen olacak bir yarış durumu görüyoruz. Bununla birlikte, bu soruda yayınlanan kod, bir koşul değişkeni kullanmanın temel kurallarından birini ihlal ediyor: Bir kontrol ve bekle gerçekleştirilirken tek bir kritik bölüm tutmaz.

Bu örnekte kod şuna benzer:

if (--f->counter == 0)      // (1)
    // we have zeroed this fence's counter, wake up everyone that waits
    f->resume.notify_all(); // (2)
else
{
    unique_lock<mutex> lock(f->resume_mutex);
    f->resume.wait(lock);   // (3)
}

Tutarken wait()# 3'te yapıldığını fark edeceksiniz f->resume_mutex. Ama olsun veya olmasın onay wait()1. adımda gerekli olan edilir değil durum değişkenleri doğru kullanımı) için bir gereksinimdir (çok daha az sürekli kontrol-ve bekleme için) hiç o kilidi, tutarken yapılır. O kod parçası ile sorunu olan kişi beri düşündük inanıyoruz f->counterbir oldu std::atomictürü bu gereksinimi karşılamak olacaktır. Ancak, tarafından sağlanan atomiklik std::atomicsonraki çağrıya kadar uzanmaz f->resume.wait(lock). Bu örnekte, ne zaman f->counterkontrol edileceği (adım # 1) ve ne zaman wait()çağrıldığı (adım # 3) arasında bir yarış vardır .

Bu soru örneğinde o ırk yok.


2
daha derin etkileri vardır: domaigne.com/blog/computing/… Özellikle, bahsettiğiniz pthread sorunu ya daha yeni bir sürümle ya da doğru bayraklarla oluşturulmuş bir sürümle çözülmelidir. ( wait morphingoptimizasyonu etkinleştirmek için ) Bu bağlantıda açıklanan temel kural: Daha öngörülebilir sonuçlar için 2'den fazla iş parçacığı olan durumlarda İLE kilitle bildirmek daha iyidir.
v.oddou

6
@Michael: Anladığım kadarıyla tüketicinin sonunda araması gerekiyor the_condition_variable.wait(lock);. Üretici ve tüketiciyi senkronize etmek için herhangi bir kilit gerekmiyorsa (temelde kilitlenmeyen bir spsc kuyruğu olduğunu söyleyin), o zaman üretici kilitlemiyorsa bu kilit bir işe yaramaz. Benim için iyi. Ama ender bir yarış için risk yok mu? Üretici kilidi tutmazsa, tüketici beklemeden hemen önceyken notify_one arayamaz mı? Sonra tüketici beklemeye başlar ve uyanmaz ...
Cookie

1
Örneğin, yukarıdaki kodda, std::cout << "Waiting... \n";üreticinin yaptığı sırada tüketicinin bulunduğunu söyleyin cv.notify_one();, ardından uyandırma çağrısı kaybolur ... Yoksa burada bir şey mi eksik?
Cookie

1
@Kurabiye. Evet, orada bir yarış durumu var. Bkz stackoverflow.com/questions/20982270/...
EH9

1
@ eh9: Lanet olsun, yorumunuz sayesinde zaman zaman kodumu donduran bir hatanın nedenini buldum. Bu tam olarak yarış durumundan kaynaklanıyordu. Bildirim tamamen çözüldükten sonra muteksin kilidini açmak sorunu tamamen çözdü ... Çok teşekkürler!
galinette

10

Durum

Vc10 ve Boost 1.56 kullanarak, bu blog gönderisinin önerdiği gibi eşzamanlı bir kuyruk oluşturdum . Yazar, çekişmeyi en aza indirmek için muteksin kilidini açar, yani notify_one()muteks kilidi açıkken çağrılır:

void push(const T& item)
{
  std::unique_lock<std::mutex> mlock(mutex_);
  queue_.push(item);
  mlock.unlock();     // unlock before notificiation to minimize mutex contention
  cond_.notify_one(); // notify one waiting thread
}

Muteksin kilidinin açılması, Boost belgelerinde bir örnekle desteklenmektedir :

void prepare_data_for_processing()
{
    retrieve_data();
    prepare_data();
    {
        boost::lock_guard<boost::mutex> lock(mut);
        data_ready=true;
    }
    cond.notify_one();
}

Sorun

Yine de bu, aşağıdaki düzensiz davranışa yol açtı:

  • süre notify_one()gelmiştir değil çağrılmış henüz cond_.wait()hala kesilebilirboost::thread::interrupt()
  • bir kez notify_one()ilk kez cond_.wait()kilitlenmeler çağrıldı ; Bekleme sona edilemez boost::thread::interrupt()veya boost::condition_variable::notify_*()artık.

Çözüm

Satırın kaldırılması mlock.unlock()kodun beklendiği gibi çalışmasını sağladı (bildirimler ve kesintiler beklemeyi sona erdirir). Not notify_one()kapsamını ayrılırken hala kilitli mutex ile çağrılır, doğru sonradan kilidi:

void push(const T& item)
{
  std::lock_guard<std::mutex> mlock(mutex_);
  queue_.push(item);
  cond_.notify_one(); // notify one waiting thread
}

Bu, en azından benim özel iş parçacığı uygulamamda boost::condition_variable::notify_one(), her iki yol da doğru görünse de, çağrmadan önce muteksin kilidinin açılmaması gerektiği anlamına gelir .


Bu sorunu Boost.Thread'e bildirdiniz mi? Orada benzer bir görev bulamıyorum svn.boost.org/trac/boost/…
magras

@magras Maalesef bunu düşünmediğime dair hiçbir fikrim yok. Ve maalesef belirtilen sırayı kullanarak bu hatayı yeniden üretmeyi başaramıyorum.
Matthäus Brandl

Erken uyanmanın ne kadar kilitlenmeye neden olabileceğinden emin değilim. Spesifik olarak, push () kuyruk muteksini serbest bıraktıktan sonra, ancak notify_one () çağrılmadan önce pop () içinde cond_.wait () 'den çıkarsanız - Pop () kuyruğu boş değil görmeli ve yeni girişi kullanmalıdır bekle () ing. push () kuyruğu güncellerken cond_.wait () 'den çıkarsanız, kilit push () ile tutulmalıdır, bu nedenle pop () kilidin serbest bırakılmasını beklemeyi engellemelidir. Diğer erken uyanmalar kilidi tutacak ve pop () bir sonraki wait () 'i çağırmadan önce push ()' nin kuyruğu değiştirmesini engelleyecektir. Ne kaçırdım?
Kevin

5

Diğerlerinin de belirttiği gibi notify_one(), yarış koşulları ve iş parçacığı ile ilgili sorunlar açısından arama sırasında kilidi tutmanıza gerek yoktur . Ancak bazı durumlarda, çağrılmadan condition_variableönce hasar görmesini önlemek için kilidin tutulması gerekebilir notify_one(). Aşağıdaki örneği düşünün:

thread t;

void foo() {
    std::mutex m;
    std::condition_variable cv;
    bool done = false;

    t = std::thread([&]() {
        {
            std::lock_guard<std::mutex> l(m);  // (1)
            done = true;  // (2)
        }  // (3)
        cv.notify_one();  // (4)
    });  // (5)

    std::unique_lock<std::mutex> lock(m);  // (6)
    cv.wait(lock, [&done]() { return done; });  // (7)
}

void main() {
    foo();  // (8)
    t.join();  // (9)
}

Yeni oluşturulan iş parçacığı için tonu oluşturduktan sonra ancak koşul değişkenini beklemeye başlamadan önce ((5) ile (6) arasında bir yerde) bir bağlam anahtarı olduğunu varsayalım . İplik tkilidi (1) alır, dayanak değişkenini (2) ayarlar ve ardından kilidi (3) serbest bırakır. notify_one()(4) yürütülmeden hemen önce bu noktada başka bir bağlam anahtarı olduğunu varsayalım . Ana iş parçacığı kilidi (6) alır ve satırı (7) çalıştırır, bu noktada yüklem geri döner trueve beklemek için bir neden yoktur, bu nedenle kilidi serbest bırakır ve devam eder. foodöner (8) ve kapsamındaki (dahil cv) değişkenler yok edilir. İş parçacığı tana iş parçacığına (9) katılmadan önce yürütmesini bitirmesi gerekir, böylece yürütmek için kaldığı yerden devam edercv.notify_one()(4), bu noktada cvzaten yok edildi!

Bu durumda olası düzeltme, arama sırasında kilidi tutmaya devam etmektir notify_one(yani (3) satırında biten kapsamı kaldırmak). Bunu yaparak, daha önce iş parçacığı tçağrılarının yeni ayarlanmış yüklem değişkenini kontrol edip devam edebilmesini sağlıyoruz çünkü  kontrolü yapmak için şu anda tutmakta olan kilidi alması gerekecek . Böylece, dönüşlerden sonra iş parçacığına erişilmemesini sağlıyoruz .notify_onecv.waittcvtfoo

Özetlemek gerekirse, bu özel durumdaki sorun gerçekten iş parçacığı ile ilgili değil, referans tarafından yakalanan değişkenlerin yaşam süreleriyle ilgilidir. cviş parçacığı aracılığıyla referansla yakalanır t, bu nedenle cviş parçacığının yürütülmesi süresince canlı kaldığından emin olmalısınız . Çünkü burada sunulan diğer örnekler, bu konuda muzdarip olmayan condition_variableve mutexnesneleri küresel alanında tanımlı dolayısıyla onlar Program çıkar kadar ayakta tutulmaya garanti edilir.


1

@Michael Burr doğru. condition_variable::notify_onedeğişken üzerinde bir kilit gerektirmez. Yine de, örnekte gösterildiği gibi, bu durumda hiçbir şey kilit kullanmanızı engelleyemez.

Verilen örnekte kilit, değişkenin eşzamanlı kullanımıyla motive edilir i. İş signalsparçacığı değişkeni değiştirdiği için, bu süre içinde başka hiçbir iş parçacığının ona erişmediğinden emin olması gerekir.

Kilitler senkronizasyon gerektiren herhangi bir durumda kullanılır , daha genel bir şekilde ifade edebileceğimizi sanmıyorum.


Tabii ki, ama bunun da ötesinde, tüm modelin gerçekten çalışabilmesi için koşul değişkenleriyle birlikte kullanılması gerekir. özellikle koşul değişkeni waitişlevi, çağrı içindeki kilidi serbest bırakır ve yalnızca kilidi yeniden aldıktan sonra geri döner. Bu noktadan sonra "okuma haklarını" elde ettiğiniz için durumunuzu güvenle kontrol edebilirsiniz. Hala beklediğiniz şey değilse, geri dönersiniz wait. bu modeldir. btw, bu örnek buna saygı DEĞİLDİR.
v.oddou

1

Bazı durumlarda, cv başka iş parçacıkları tarafından işgal edildiğinde (kilitlendiğinde). _ * () Bildiriminde bulunmadan önce kilitlenip serbest bırakmanız gerekir.
Değilse, bildirim _ * () hiç yürütülmemiş olabilir.


1

Sadece bu cevabı ekledim çünkü kabul edilen cevabın yanıltıcı olabileceğini düşünüyorum. Her durumda , kodunuzun iş parçacığı açısından güvenli olması için notify_one () 'yu bir yere çağırmadan önce muteksi kilitlemeniz gerekecektir , ancak aslında notify _ * ()' yı çağırmadan önce tekrar kilidini açabilirsiniz.

Açıklığa kavuşturmak için, wait (lk) 'ye girmeden önce kilidi ALMALISINIZ çünkü wait (), lk'nin kilidini açar ve kilit kilitli değilse Tanımsız Davranış olur. Notify_one () ile durum böyle değildir, ancak wait () girmeden ve bu çağrının muteksin kilidini açmasını sağlamadan önce notify _ * () 'ı çağırmayacağınızdan emin olmanız gerekir ; ki bu tabii ki sadece aynı muteksi sen notify _ * () çağırmadan önce kilitleyerek yapılabilir.

Örneğin, aşağıdaki durumu düşünün:

std::atomic_int count;
std::mutex cancel_mutex;
std::condition_variable cancel_cv;

void stop()
{
  if (count.fetch_sub(1) == -999) // Reached -1000 ?
    cv.notify_one();
}

bool start()
{
  if (count.fetch_add(1) >= 0)
    return true;
  // Failure.
  stop();
  return false;
}

void cancel()
{
  if (count.fetch_sub(1000) == 0)  // Reached -1000?
    return;
  // Wait till count reached -1000.
  std::unique_lock<std::mutex> lk(cancel_mutex);
  cancel_cv.wait(lk);
}

Uyarı : Bu kod bir hata içeriyor.

Fikir şudur: evreler çiftler halinde start () ve stop () 'u çağırırlar, ancak sadece start () true döndürdüğü sürece. Örneğin:

if (start())
{
  // Do stuff
  stop();
}

Bir (diğer) iş parçacığı bir noktada cancel () 'yi çağıracak ve cancel ()' den döndükten sonra 'Do stuff' için gereken nesneleri yok edecek. Ancak, start () ve stop () arasında iş parçacıkları varken cancel () geri dönmemelidir ve cancel () ilk satırını çalıştırdığında, start () her zaman false döndürür, bu nedenle yeni iş parçacıkları 'Do malzeme alanı.

Doğru çalışıyor mu?

Gerekçe şu şekildedir:

1) Herhangi bir evre, start () 'ın ilk satırını başarıyla yürütürse (ve bu nedenle true döndürürse), henüz hiçbir evre ilk cancel () satırını çalıştırmadı (toplam iş parçacığı sayısının 1000'den çok daha küçük olduğunu varsayıyoruz. yol).

2) Ayrıca, bir iş parçacığı start'ın () ilk satırını başarıyla yürütürken, ancak henüz ilk stop satırını () gerçekleştirmemişken, herhangi bir iş parçacığının cancel () 'nin ilk satırını başarıyla yürütmesi imkansızdır (yalnızca bir iş parçacığının ever çağrıları cancel ()): fetch_sub (1000) tarafından döndürülen değer 0'dan büyük olacaktır.

3) Bir iş parçacığı cancel () 'nin ilk satırını çalıştırdığında, başlangıç ​​satırının () ilk satırı her zaman yanlış döndürür ve start ()' ı çağıran bir iş parçacığı artık 'İşleri yap' alanına girmez.

4) Başlatılacak () ve durdurulacak () çağrıların sayısı her zaman dengelidir, bu nedenle ilk iptal satırı () başarısız bir şekilde yürütüldükten sonra, her zaman bir (son) durdurma çağrısının () sayıya neden olduğu bir an olacaktır. -1000'e ulaşmak ve dolayısıyla çağrılacak bildir_bir (). Unutmayın ki, yalnızca ilk iptal satırı bu iş parçacığı ile sonuçlandığında gerçekleşebilir.

Bu kadar çok iş parçacığının start () / stop () 'u çağırdığı ve sayımın asla -1000'e ulaşmadığı ve cancel ()' nin asla geri dönmediği bir açlık probleminin yanı sıra, birinin "olası olmayan ve asla uzun sürmeyen" olarak kabul edebileceği başka bir hata daha var:

'İşleri yap' alanında bir iş parçacığı olması mümkündür, diyelim ki sadece stop () çağırıyor; o anda bir evre, fetch_sub (1000) ile 1 değerini okuyan ve içinden düşen cancel () 'nin ilk satırını yürütür. Ancak muteksi almadan ve / veya bekleme çağrısını yapmadan (lk) önce, ilk evre stop () 'un ilk satırını çalıştırır, -999 okur ve cv.notify_one ()!

Sonra bu notify_one () çağrısı, koşul değişkeninde beklemeden () ÖNCE yapılır! Ve program süresiz olarak kilitlenecektir.

Bu nedenle biz () notify_one çağırmak mümkün olmamalıdır kadar biz beklemek denir (). Bir koşul değişkeninin gücünün, muteksin atomik olarak kilidini açabilmesinde yattığını, notify_one () için bir çağrının olup olmadığını ve uykuya geçip geçmediğini kontrol ettiğini unutmayın. Bunu aptal olamaz, ancak bunu sen yanlıştan doğruya durumunu değiştirip olabilecek faktörler değişiklik yaptığınızda muteks kilitli tutma gereğini tutmak Burada anlatılan gibi çünkü yarış koşullarının notify_one () çağrısında kilitli.

Ancak bu örnekte herhangi bir koşul yoktur. Neden 'count == -1000' koşulu olarak kullanmadım? Çünkü burada bu hiç ilginç değil: -1000'e ulaşılır ulaşılmaz, 'İşler yap' alanına yeni bir iş parçacığı girmeyeceğinden eminiz. Dahası, iş parçacıkları hala start () çağırabilir ve sayımı artıracaktır (-999 ve -998'e kadar), ancak biz bunu umursamıyoruz. Önemli olan tek şey -1000'e ulaşılmış olmasıdır - böylece artık 'İşleri yap' alanında hiçbir iş parçacığı olmadığından emin olabiliriz. Notify_one () çağrılırken durumun bu olduğundan eminiz, ancak cancel () muteksini kilitlemeden önce notify_one () 'yu çağırmadığımızdan nasıl emin olabiliriz? Sadece notify_one () 'dan kısa süre önce cancel_mutex'i kilitlemek elbette yardımcı olmayacak.

Sorun, bir durum için bekliyor değiliz buna rağmen hala orada, yani olan bir durumdur ve biz mutex'i kilitlemek gerekiyor

1) bu koşula ulaşılmadan önce 2) notify_one çağrılmadan önce.

Dolayısıyla doğru kod şu olur:

void stop()
{
  if (count.fetch_sub(1) == -999) // Reached -1000 ?
  {
    cancel_mutex.lock();
    cancel_mutex.unlock();
    cv.notify_one();
  }
}

[... aynı başlangıç ​​() ...]

void cancel()
{
  std::unique_lock<std::mutex> lk(cancel_mutex);
  if (count.fetch_sub(1000) == 0)
    return;
  cancel_cv.wait(lk);
}

Tabii ki bu sadece bir örnek ama diğer durumlar birbirine çok benziyor; Eğer bir koşullu değişken kullanın hemen her durumda ihtiyaç olduğunu muteksi olması) notify_one (çağırmadan önce (kısaca) kilitli, aksi takdirde sen beklemek çağırmadan önce () diyoruz olması mümkündür.

Bu durumda notify_one () 'yu çağırmadan önce muteksin kilidini açtığıma dikkat edin, çünkü aksi takdirde notify_one () çağrısının koşul değişkenini bekleyen iş parçacığını uyandırması (küçük) şansı vardır ve bu daha sonra muteksi almaya çalışır ve Mutex'i tekrar serbest bırakmadan önce blok. Bu, gerekenden biraz daha yavaş.

Bu örnek, koşulu değiştiren satır wait () 'i çağıran aynı evre tarafından yürütüldüğü için biraz özeldi.

Daha olağan olan durum, bir iş parçacığının bir koşulun gerçek olmasını beklediği ve başka bir iş parçacığının bu koşulda yer alan değişkenleri değiştirmeden önce kilidi aldığı (muhtemelen doğru olmasına neden olduğu) durumdur. Bu durumda muteks , koşul gerçekleşmeden hemen önce (ve sonra) kilitlenir - bu nedenle, bu durumda notify _ * () 'yı çağırmadan önce muteksin kilidini açmak tamamen uygundur.

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.