共用方式為


如何:使用內容類別實作合作式信號

本主題說明如何使用 concurrency::Context 類別來實作合作號誌類別。

備註

類別 Context 可讓您封鎖或產生目前的執行內容。 當目前內容無法繼續時,封鎖或產生目前的內容很有用,因為資源無法使用。 旗號是目前執行內容必須等候資源可供使用之一情況的範例。 像重要區段對象一樣,旗號是同步處理物件,可讓一個內容中的程式代碼具有資源的獨佔存取權。 不過,與重要區段物件不同,旗號可讓多個內容同時存取資源。 如果內容數目上限保留號誌鎖定,則每個額外的內容必須等候另一個內容釋放鎖定。

實作號誌類別

  1. 宣告名為 semaphore的類別。 將和 private 區段新增public至此類別。
// A semaphore type that uses cooperative blocking semantics.
class semaphore
{
public:
private:
};
  1. 在 類別的 區private段中,宣告保存號誌計數的 std::atomic 變數,以及保存必須等候取得旗號的內容的並行::concurrent_queue semaphore 物件。
// 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;
  1. 在 類別的 semaphorepublic段中,實作 建構函式。 建構函式會接受值 long long ,指定可以同時保存鎖定的內容數目上限。
explicit semaphore(long long capacity)
   : _semaphore_count(capacity)
{
}
  1. 在 類別的 semaphorepublic段中,實acquire作 方法。 這個方法會將號誌計數遞減為不可部分完成的作業。 如果信號計數變成負數,請將目前的內容新增至等候佇列的結尾,並呼叫 concurrency::Context::Block 方法來封鎖目前的內容。
// 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();
   }
}
  1. 在 類別的 semaphorepublic段中,實release作 方法。 這個方法會將號誌計數遞增為不可部分完成的作業。 如果遞增作業前的號誌計數為負數,則至少有一個正在等候鎖定的內容。 在此情況下,請解除封鎖等候佇列前方的內容。
// 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();
   }
}

範例

semaphore此範例中的 類別會合作運作,因為 Context::BlockContext::Yield 方法會產生執行,讓運行時間可以執行其他工作。

acquire方法會遞減計數器,但在另一個內容呼叫 release 方法之前,它可能不會完成將內容新增至等候佇列。 為了考慮到這一點, release 此方法會使用微調循環來呼叫 並行::Context::Yield 方法,以等候 acquire 方法完成新增內容。

方法release可以在方法呼叫 方法之前acquire呼叫 Context::Unblock Context::Block 方法。 您不需要保護此競爭條件,因為運行時間允許依任何順序呼叫這些方法。 release如果方法在方法呼叫相同內容之前acquire呼叫 Context::Unblock Context::Block ,該內容會保持解除封鎖。 運行時間只需要將 的每個呼叫 Context::Block 都與 對應的呼叫 Context::Unblock相符。

下列範例顯示完整的 semaphore 類別。 函 wmain 式會顯示此類別的基本用法。 函 wmain 式會使用 concurrency::p arallel_for 演演算法來建立數個需要存取旗號的工作。 因為三個線程可以隨時保存鎖定,某些工作必須等候另一個工作完成並釋放鎖定。

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

此範例會產生下列範例輸出。

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

如需 類別 concurrent_queue 的詳細資訊,請參閱 平行容器和物件。 如需演算法 parallel_for 的詳細資訊,請參閱 平行演算法

編譯程式碼

複製範例程式代碼,並將其貼到 Visual Studio 專案中,或貼到名為 cooperative-semaphore.cpp 的檔案中,然後在 Visual Studio 命令提示字元視窗中執行下列命令。

cl.exe /EHsc cooperative-semaphore.cpp

穩固程式設計

您可以使用資源 擷取是初始化 (RAII) 模式,將物件的存取 semaphore 限制在指定的範圍。 在RAII模式下,會在堆疊上配置數據結構。 該數據結構會在建立資源時初始化或取得資源,並在數據結構終結時終結或釋放該資源。 RAII 模式保證解構函式會在封入範圍結束之前呼叫。 因此,當擲回例外狀況或函式包含多個 return 語句時,資源就會正確管理。

下列範例會定義名為 scoped_lock的類別,該類別定義於 類別的 public semaphore 區段中。 類別 scoped_lock 類似於 並行::critical_section::scoped_lock並行::reader_writer_lock::scoped_lock 類別。 類別的 semaphore::scoped_lock 建構函式會取得指定 semaphore 物件的存取權,而解構函式會釋放該物件的存取權。

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

下列範例會修改傳遞至 parallel_for 演算法的工作函式主體,讓它使用RAII確保旗號在函式傳回之前釋放。 這項技術可確保工作函式是安全的例外狀況。

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

另請參閱

內容
平行容器和物件