Sdílet prostřednictvím


Postupy: Použití třídy kontextu pro implementaci semaforu pro spolupráci

Toto téma ukazuje, jak použít concurrency::Context třídy pro implementaci třídy spolupráce semafor.

Context Třída umožňuje blokovat nebo výnos aktuální kontext spuštění.Blokování nebo získávání aktuální kontext je užitečné při aktuální kontext nelze pokračovat, protože zdroj není k dispozici.A semafor je příkladem popíšeme situaci, kdy aktuální kontext spuštění musí čekat na zdroj k dispozici.Semafor, stejně jako důležité část objektu, je objekt zajišťující synchronizaci, která umožňuje kódu v kontextu jednoho mít výhradní přístup k prostředku.Na rozdíl od objektu kritická sekce umožňuje semafor více než jeden kontext pro přístup k prostředku souběžně.Pokud maximální počet kontextů obsahuje zámek semafor, každý další kontextu čekat jiného kontextu uvolnění uzamčení.

Implementace třídy semaphore

  1. Deklarujte třídu s názvem semaphore.Přidat public a private oddíly do této třídy.

    // A semaphore type that uses cooperative blocking semantics. 
    class semaphore
    {
    public:
    private:
    };
    
  2. V private část semaphore třídy, deklarace std::atomic proměnná, která obsahuje počet semafor a concurrency::concurrent_queue objekt, který obsahuje kontexty, které musí čekat na získání semaforu.

    // The semaphore count.
    atomic<long long> _semaphore_count;
    
    // A concurrency-safe queue of contexts that must wait to  
    // acquire the semaphore.
    concurrent_queue<Context*> _waiting_contexts;
    
  3. V public sekce semaphore třídy, které implementují konstruktoru.Konstruktor přijímá long long hodnota, která určuje maximální počet kontextů, které můžete současně podržte zámek.

    explicit semaphore(long long capacity)
       : _semaphore_count(capacity)
    {
    }
    
  4. V public část semaphore třídy, které implementují acquire metody.Tato metoda snižuje semafor počítat jako atomická operace.Pokud počet semafor bude negativní, přidat aktuální kontext na konec fronty čekání a volání concurrency::Context::Block metoda blokování aktuální kontext.

    // Acquires access to the semaphore. 
    void acquire()
    {
       // The capacity of the semaphore is exceeded when the semaphore count  
       // falls below zero. When this happens, add the current context to the  
       // back of the wait queue and block the current context. 
       if (--_semaphore_count < 0)
       {
          _waiting_contexts.push(Context::CurrentContext());
          Context::Block();
       }
    }
    
  5. V public část semaphore třídy, které implementují release metody.Tato metoda se zvýší počet semafor jako atomická operace.Pokud počet semafor je záporná před operací přírůstek, existuje alespoň jeden kontext, ve kterém je čekání zámek.V tomto případě odblokovat kontextu, který je na přední straně fronty čekání.

    // Releases access to the semaphore. 
    void release()
    {
       // If the semaphore count is negative, unblock the first waiting context. 
       if (++_semaphore_count <= 0)
       {
          // A call to acquire might have decremented the counter, but has not 
          // yet finished adding the context to the queue.  
          // Create a spin loop that waits for the context to become available.
          Context* waiting = NULL;
          while (!_waiting_contexts.try_pop(waiting))
          {
             Context::Yield();
          }
    
          // Unblock the context.
          waiting->Unblock();
       }
    }
    

Příklad

semaphore Třídy v tomto příkladu chovat kooperativně, protože Context::Block a Context::Yield metody poskytují spuštění tak, aby modul runtime může provádět další úkoly.

acquire Metoda sníží čítač, ale nemusí být dokončeno přidání čekací fronty před volání jiné kontextu kontextu release metody.Na to, release metoda používá otočný smyčky, která volá concurrency::Context::Yield metoda čekat acquire metoda dokončíte přidávání kontextu.

release Metoda lze volat Context::Unblock metoda před acquire volání metody Context::Block metody.Není nutné chránit před tento spor, protože modul runtime umožňuje tyto metody má být volána v libovolném pořadí.Pokud release volání metody Context::Unblock před acquire volání metody Context::Block stejného kontextu této souvislosti zůstane odblokována.Modul runtime vyžaduje pouze každé volání na Context::Block je porovnána s odpovídající volání Context::Unblock.

Následující příklad ukazuje kompletní semaphore třídy.wmain Funkce zobrazuje základní použití této třídy.wmain Funkce používá concurrency::parallel_for algoritmus vytvořit několik úkolů, které vyžadují přístup k semaforu.Vzhledem k tomu, že tři podprocesy může pojmout zámek kdykoli, některé úkoly musí čekat jiný úkol dokončit a uvolnit uzamčení.

// cooperative-semaphore.cpp 
// compile with: /EHsc
#include <atomic>
#include <concrt.h>
#include <ppl.h>
#include <concurrent_queue.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// A semaphore type that uses cooperative blocking semantics. 
class semaphore
{
public:
   explicit semaphore(long long capacity)
      : _semaphore_count(capacity)
   {
   }

   // Acquires access to the semaphore. 
   void acquire()
   {
      // The capacity of the semaphore is exceeded when the semaphore count  
      // falls below zero. When this happens, add the current context to the  
      // back of the wait queue and block the current context. 
      if (--_semaphore_count < 0)
      {
         _waiting_contexts.push(Context::CurrentContext());
         Context::Block();
      }
   }

   // Releases access to the semaphore. 
   void release()
   {
      // If the semaphore count is negative, unblock the first waiting context. 
      if (++_semaphore_count <= 0)
      {
         // A call to acquire might have decremented the counter, but has not 
         // yet finished adding the context to the queue.  
         // Create a spin loop that waits for the context to become available.
         Context* waiting = NULL;
         while (!_waiting_contexts.try_pop(waiting))
         {
            Context::Yield();
         }

         // Unblock the context.
         waiting->Unblock();
      }
   }

private:
   // The semaphore count.
   atomic<long long> _semaphore_count;

   // A concurrency-safe queue of contexts that must wait to  
   // acquire the semaphore.
   concurrent_queue<Context*> _waiting_contexts;
};

int wmain()
{
   // Create a semaphore that allows at most three threads to  
   // hold the lock.
   semaphore s(3);

   parallel_for(0, 10, [&](int i) {
      // Acquire the lock.
      s.acquire();

      // Print a message to the console.
      wstringstream ss;
      ss << L"In loop iteration " << i << L"..." << endl;
      wcout << ss.str();

      // Simulate work by waiting for two seconds.
      wait(2000);

      // Release the lock.
      s.release();
   });
}

Tento příklad vytvoří následující vzorový výstup.

  

Další informace o třídě concurrent_queue naleznete v tématu Paralelní kontejnery a objekty.Další informace o parallel_for algoritmus, viz Paralelní algoritmy.

Probíhá kompilace kódu

Zkopírovat ukázkový kód a vložit jej do projektu sady Visual Studio nebo vložit do souboru s názvem družstvo semaphore.cpp a potom spusťte následující příkaz v okně Příkazový řádek Visual Studio.

cl.exe /EHsc cooperative-semaphore.cpp

Robustní programování

Lze použít Inicializace je pořízení prostředků vzorek (RAII) pro omezení přístupu ke semaphore objekt daného oboru.Podle vzoru RAII struktura dat je přidělený do zásobníku.Že struktura dat inicializuje nebo získá prostředek při vytvoření a ničí nebo uvolní zdroje v případě, že datová struktura je zničen.Vzorek RAII zaručuje, že je zavolán destruktor před ukončí ohraničujícím obor.Proto zdroj správně spravovat je vyvolána výjimka nebo funkce obsahuje více return příkazy.

Následující příklad definuje třídu s názvem scoped_lock, který je definován v public sekce semaphore třídy.scoped_lock Se podobá třídě concurrency::critical_section::scoped_lock a concurrency::reader_writer_lock::scoped_lock třídy.Konstruktor semaphore::scoped_lock třídy získá přístup k dané semaphore objekt a destruktor uvolní přístup k danému objektu.

// An exception-safe RAII wrapper for the semaphore class. 
class scoped_lock
{
public:
   // Acquires access to the semaphore.
   scoped_lock(semaphore& s)
      : _s(s)
   {
      _s.acquire();
   }
   // Releases access to the semaphore.
   ~scoped_lock()
   {
      _s.release();
   }

private:
   semaphore& _s;
};

Následující příklad upravuje těla funkce práce, který je předán parallel_for algoritmus tak, aby používala RAII zajistit, že semafor je vydán dříve, než se vrátí.Tento postup zajišťuje, že pracovní funkce bezpečné výjimku.

parallel_for(0, 10, [&](int i) {
   // Create an exception-safe scoped_lock object that holds the lock  
   // for the duration of the current scope.
   semaphore::scoped_lock auto_lock(s);

   // Print a message to the console.
   wstringstream ss;
   ss << L"In loop iteration " << i << L"..." << endl;
   wcout << ss.str();

   // Simulate work by waiting for two seconds.
   wait(2000);
});

Viz také

Referenční dokumentace

Context – třída

Koncepty

Kontexty

Paralelní kontejnery a objekty