共用方式為


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

本主題說明如何使用 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::UnblockContext::Block 方法。 您不需要保護此競爭條件,因為執行時間允許依任何順序呼叫這些方法。 release如果方法在方法呼叫相同內容之前 acquire 呼叫 Context::UnblockContext::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 的類別,該類別定義于 類別的 publicsemaphore 區段中。 類別 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);
});

另請參閱

內容
平行容器和物件