Udostępnij za pośrednictwem


Jak: klasa kontekstu służy do wprowadzenia w życie wspólnych semafora

W tym temacie przedstawiono sposób użycia concurrency::Context klasy implementującej klasy współpracy semafora.

Context Klasy pozwala zablokować lub bieżący kontekst wykonywania plon.Blokowanie lub nadającego bieżącego kontekstu jest przydatne podczas bieżącego kontekstu nie może kontynuować, ponieważ zasób jest niedostępny.A semafora jest przykładem jednego sytuacji, gdy bieżący kontekst wykonywania musi czekać na udostępnienie zasobu.Semafor, podobnie jak obiekt sekcji krytycznej, jest obiektem synchronizacji umożliwiający kodu w kontekście jednego wyłącznego dostępu do zasobu.W przeciwieństwie do obiektu sekcji krytycznej, semafor umożliwia więcej niż jednym kontekście jednocześnie dostęp do zasobu.Jeśli maksymalna liczba kontekstów posiada blokadę semafora, każdy dodatkowy kontekst musi czekać w innym kontekście zwolnić blokadę.

Aby zaimplementować klasę semafora

  1. Zadeklarować klasy o nazwie semaphore.Dodaj public i private sekcji do tej klasy.

    // A semaphore type that uses cooperative blocking semantics.
    class semaphore
    {
    public:
    private:
    };
    
  2. W private sekcji semaphore klasy, oświadczyć, std::atomic zmienną przechowującą Licznik semafora i concurrency::concurrent_queue obiekt, który przechowuje kontekstów, które muszą czekać na uzyskanie semafora.

    // 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. W public sekcji semaphore klasy, implementować konstruktora.Trwa konstruktora long long wartość określa maksymalną liczbę kontekstów, które mogą zawierać jednocześnie blokady.

    explicit semaphore(long long capacity)
       : _semaphore_count(capacity)
    {
    }
    
  4. W public sekcji semaphore klasy, zaimplementować acquire metody.Zmniejsza to metoda semafor jest liczone jako atomowej operacji.Jeśli licznik semafora staje się ujemna, dodać bieżący kontekst na koniec kolejki oczekiwania i wywołanie concurrency::Context::Block metody zablokować bieżącego kontekstu.

    // 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. W public sekcji semaphore klasy, zaimplementować release metody.Metoda ta zwiększa licznik semafora atomowej operacji.Jeśli licznik semafora jest ujemna przed operacją przyrostu, istnieje co najmniej jeden kontekst czeka na blokadę.W takim przypadku odblokować kontekst na początku kolejki oczekiwania.

    // 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();
       }
    }
    

Przykład

semaphore Klasy w tym przykładzie zachowuje się wspólnie ponieważ Context::Block i Context::Yield metody dają wykonanie, tak aby wykonawczym można wykonywać inne zadania.

acquire Metody zmniejsza licznik, ale może nie zakończyć dodawanie do kolejki oczekiwania przed wywołaniami w innym kontekście kontekście release metody.Aby uwzględnić to, release metoda wykorzystuje pętli pokrętła, który wywołuje concurrency::Context::Yield metody oczekiwania acquire metody, aby zakończyć dodawanie kontekstu.

release Można wywołać metody Context::Unblock metoda przed acquire wywołania metody Context::Block metody.Nie masz ochronę przed sytuacja wyścigu, ponieważ umożliwia wykonywania tych metod wywołać w dowolnym porządku.Jeśli release wywołania metody Context::Unblock przed acquire wywołania metody Context::Block w tym samym kontekście tego kontekstu pozostaje odblokowana.Środowiska wykonawczego wymaga jedynie, że każdy wywołanie Context::Block jest dopasowywany do odpowiedniego wywołania Context::Unblock.

Poniższy przykład pokazuje pełną semaphore klasy.wmain Funkcja zawiera podstawowe użycie tej klasy.wmain Działać zastosowań concurrency::parallel_for algorytm, aby utworzyć kilka zadań, które wymagają dostępu do semafora.Ponieważ trzy wątki może pomieścić blokady w dowolnym momencie, niektóre zadania należy poczekać innego zadania Zakończ i Zwolnij blokadę.

// 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();
   });
}

Ten przykład generuje następujące przykładowe dane wyjściowe.

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...

Więcej informacji o concurrent_queue klasy, zobacz Równoległe kontenerów i obiektów.Więcej informacji o parallel_for algorytm, zobacz Algorytmy równoległe.

Kompilowanie kodu

Skopiuj przykładowy kod i wklej go w projekcie programu Visual Studio lub wkleić go w pliku o nazwie spółdzielni semaphore.cpp , a następnie uruchom następujące polecenie w oknie wiersza polecenia usługi Visual Studio.

cl.exe /EHsc cooperative-semaphore.cpp

Stabilne programowanie

Można użyć Zasobów nabycia jest inicjowania deseń (RAII), aby ograniczyć dostęp do semaphore obiektu do danego zakresu.W obszarze wzorzec RAII struktura danych jest przydzielane na stosie.Struktury danych inicjuje lub nabywa zasobu podczas tworzenia i niszczy lub zwalniany jest niszczony, struktura danych tego zasobu.Deseń RAII gwarancje destruktor nosi przed zamyka zasięgu.Dlatego zasobu jest poprawnie zarządzane wyjątek lub funkcja zawiera wiele return instrukcji.

W poniższym przykładzie zdefiniowano klasę o nazwie scoped_lock, który jest zdefiniowany w public sekcji semaphore klasy.scoped_lock Podobny do klasy concurrency::critical_section::scoped_lock i concurrency::reader_writer_lock::scoped_lock klasy.Konstruktor semaphore::scoped_lock klasy uzyskuje dostęp do danej semaphore obiektu i destruktor zwalnia dostęp do tego obiektu.

// 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;
};

Poniższy przykład modyfikuje organ, który jest przekazywany do funkcji pracy parallel_for algorytm, tak że używa RAII do zapewnienia zwolnienia semafora, zanim funkcja zwraca.Technika ta zapewnia, że funkcja pracy jest bezpieczne wyjątek.

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);
});

Zobacz też

Informacje

Klasa kontekstu

Koncepcje

Konteksty

Równoległe kontenerów i obiektów