Sdílet prostřednictvím


Jak: použití třídy kontext pro provádění spolupráce semafor

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

Context Třída umožňuje blokovat nebo výnos aktuálního kontextu 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 jednoho situace, kdy aktuální kontext spuštění musí čekat na zdroj k dispozici.Semafor kritické části objektu, jako je objekt synchronizace, který umožňuje kód v kontextu jeden výhradní přístup k prostředku.Na rozdíl od kritické části objektu, umožňuje semafor více než jeden kontext pro přístup k prostředku souběžně.Maximální počet kontextů drží zámek semafor, musí každý další kontextu počkat jiného kontextu uzamčení.

Implementace třídy semafor

  1. Deklarovat 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, deklarovat std::atomic proměnná, která obsahuje počet semafor a concurrency::concurrent_queue objekt, který uchovává kontexty, které musí čekat získat semafor.

    // 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 část semaphore třídy, implementovat konstruktor.Konstruktor trvá long long hodnotu, která určuje maximální počet kontextů, které mohou současně obsahovat zámku.

    explicit semaphore(long long capacity)
       : _semaphore_count(capacity)
    {
    }
    
  4. V public část semaphore třídy, implementovat acquire metoda.Tato metoda sníží semafor počítat jako atomická operace.Pokud počet semafor negativní, přidat aktuální kontext na konec fronty čekání a volání concurrency::Context::Block metoda zablokovat aktuálním kontextu.

    // 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, implementovat release metoda.Tato metoda se zvýší počet semafor jako atomická operace.Pokud je počet semafor záporné před operací přírůstek, je alespoň jeden kontextu, který čeká na zámek.V tomto případě odblokování kontextu, který je vpředu čekací fronty.

    // 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 v tomto příkladu třídy chová zavedení, protože Context::Block a Context::Yield metody výnos spuštění tak, aby modul runtime můžete provádět další úlohy.

acquire Metoda sníží čítač, ale nemusí dokončit přidání kontext fronty čekání před jiný kontext volání release metoda.Na účet, release metoda používá spin smyčky, která volá concurrency::Context::Yield metoda čekat acquire metoda dokončíte přidávání kontextu.

release Lze volat metody Context::Unblock metoda před acquire volání metody Context::Block metoda.Není třeba chránit před tento spor, protože běhové prostředí umožňuje tyto metody volat v libovolném pořadí.Pokud release volání metody Context::Unblock před acquire volání metody Context::Block této souvislosti zůstává stejné kontextu odblokovány.Runtime vyžaduje pouze každého volání na Context::Block s odpovídající volání odpovídá Context::Unblock.

Následující příklad ukazuje úplnou semaphore třídy.wmain Funkce zobrazuje základní použití této třídy.wmain Používá funkci concurrency::parallel_for algoritmus vytvořit několik úkolů, které vyžadují přístup k semaforu.Protože tři podprocesů 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í ukázkový výstup.

In loop iteration 5...
In loop iteration 0...
In loop iteration 6...
In loop iteration 1...
In loop iteration 2...
In loop iteration 7...
In loop iteration 3...
In loop iteration 8...
In loop iteration 9...
In loop iteration 4...

Další informace o concurrent_queue třídy naleznete v tématu Paralelní kontejnerů a objektů.Další informace o parallel_for algoritmus, viz Paralelní algoritmy.

Probíhá kompilace kódu

Příklad kódu zkopírujte a vložte do projektu Visual Studio nebo vložit do souboru s názvem družstvo semaphore.cpp a spusťte následující příkaz v okně příkazového řádku Visual Studio.

cl.exe /EHsc cooperative-semaphore.cpp

Robustní programování

Můžete použít Inicializace je získání prostředků (RAII) vzorek omezit přístup k semaphore objekt daného oboru.Podle vzoru RAII je struktura dat přiřazené v zásobníku.Že struktura dat inicializuje nebo získá zdroje při vytvoření a ničí nebo po zničení strukturu dat uvolní prostředku.Vzorek RAII zaručuje, že se objekt je volána před ukončí ohraničujícím oboru.Proto zdroje je správně spravována při vyvolání výjimky nebo funkce obsahuje více return příkazy.

Následující příklad definuje třídu s názvem scoped_lock, která je definována v public část semaphore třídy.scoped_lock Se podobá třídě concurrency::critical_section::scoped_lock a concurrency::reader_writer_lock::scoped_lock třídy.Konstruktoru semaphore::scoped_lock třídy získá přístup k dané semaphore objektu a se objekt uvolní přístup k 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 subjektu, který je předán do funkce práce parallel_for algoritmus tak, že se používá RAII k zajištění, že semafor je uvolněna dříve, než se vrátí.Tato technika 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

Třída Context

Koncepty

Kontexty

Paralelní kontejnerů a objektů