Aracılığıyla paylaş


İzlenecek yol: kilitlenme önlemek için JOIN kullanmak

Bu konuda, akşam yemeği philosophers sorunu nasıl kullanılacağını göstermek için kullandığı concurrency::join uygulamanızdaki kilitlenmeyi önlemek için sınıf.Yazılım uygulamasında kilitlenme iki veya daha fazla işlemin her bir kaynak tutun ve karşılıklı olarak diğer bir kaynağı serbest bırakmak için başka bir işlem bekleyin oluşur.

Akşam yemeği philosophers sorun, birden çok eşzamanlı işlemler arasında paylaştırılmış kaynakları kümesi olduğunda oluşabilecek sorunları genel kümesinin belirli bir örnektir.

Önkoşullar

Bu izlenecek yolda, başlamadan önce aşağıdaki konuları okuyun:

Bölümler

Bu izlenecek yolda, aşağıdaki bölümleri içerir:

  • Akşam yemeği Philosophers sorunu

  • Naïve uygulaması

  • Kilitlenme önlemek için JOIN kullanmak

Akşam yemeği Philosophers sorunu

Akşam yemeği philosophers sorunu nasıl bir uygulamada kilitlenme oluşuyor göstermektedir.Bu sorun, hepsini tablosuna beş philosophers sit.Her philosopher, düşünce ve beslenme arasında seçim yapar.Her philosopher bir chopstick sol ve başka bir komşu ile paylaşmalısınız chopstick ile sağdaki komşu.Aşağıda, bu düzeni gösterilmektedir.

Yemek Yiyen Filozoflar sorunu

Yemek için bir philosopher iki chopsticks basılı tutmanız gerekir.Her philosopher tek chopstick tutan ve başka biri için bekleyen, sonra hiçbir philosopher yemek ve tüm starve.

Top

Naïve uygulaması

Aşağıdaki örnek akşam yemeği philosophers sorunun naïve uygulaması gösterir.philosopher Türetildiği sınıfı concurrency::agent, her philosopher bağımsız olarak davranmasını sağlar.Paylaşılan bir dizi örnek kullanır concurrency::critical_section nesneleri her vermek philosopher nesne chopsticks çiftinin özel kullanım erişimi.

Resimde uygulamasına ilişkilendirmek için philosopher sınıfı, bir philosopher temsil eder.Bir int değişkeni her chopstick temsil eder.critical_section Nesneleri üzerinde bekletin chopsticks sahipleri hizmet eder.run Yöntemi philosopher ömrü taklit eder.think Yöntemi düşünme eylemi taklit eder ve eat yöntemi beslenme kanun taklit eder.

A philosopher nesne kilitler hem de critical_section chopsticks önceki sahipleri tarafından kaldırılmasını benzetimini yapmak için çağıran nesneler eat yöntemi.Çağrısının eat, philosopher nesnesi döndüren chopsticks sahiplerini ayarlayarak critical_section nesneleri geri kilidi durumu.

pickup_chopsticks Yöntemi göstermektedir nerede kilitlenme oluşabilir.Her, philosopher nesne erişimi kilitler birini ve sonra Hayır kazançları philosopher diğer kilit başka tarafından denetlendiği için nesne devam edebilirler philosopher nesnesi.

Örnek

Dd742294.collapse_all(tr-tr,VS.110).gifKod

// philosophers-deadlock.cpp
// compile with: /EHsc
#include <agents.h>
#include <string>
#include <array>
#include <iostream>
#include <algorithm>
#include <random>

using namespace concurrency;
using namespace std;

// Defines a single chopstick.
typedef int chopstick;

// The total number of philosophers.
const int philosopher_count = 5;

// The number of times each philosopher should eat.
const int eat_count = 50;

// A shared array of critical sections. Each critical section 
// guards access to a single chopstick.
critical_section locks[philosopher_count];

// Implements the logic for a single dining philosopher.
class philosopher : public agent 
{
public:
   explicit philosopher(chopstick& left, chopstick& right, const wstring& name)
      : _left(left)
      , _right(right)
      , _name(name)
      , _random_generator(42)
   {
      send(_times_eaten, 0);
   }

   // Retrieves the number of times the philosopher has eaten.
   int times_eaten()
   {
      return receive(_times_eaten);
   }

   // Retrieves the name of the philosopher.
   wstring name() const
   {
      return _name;
   }

protected:
   // Performs the main logic of the dining philosopher algorithm.
   void run()
   {
      // Repeat the thinks/eat cycle a set number of times.
      for (int n = 0; n < eat_count; ++n)
      {
         think();

         pickup_chopsticks(); 
         eat();
         send(_times_eaten, n+1);
         putdown_chopsticks();
      }

      done();
   }

   // Gains access to the chopsticks.
   void pickup_chopsticks()
   {
      // Deadlock occurs here if each philosopher gains access to one
      // of the chopsticks and mutually waits for another to release
      // the other chopstick.
      locks[_left].lock();
      locks[_right].lock();
   }

   // Releases the chopsticks for others.
   void putdown_chopsticks()
   {
      locks[_right].unlock();
      locks[_left].unlock();
   }

   // Simulates thinking for a brief period of time.
   void think()
   {
      random_wait(100);
   }

   // Simulates eating for a brief period of time.
   void eat()
   { 
      random_wait(100);
   }

private:
   // Yields the current context for a random period of time.
   void random_wait(unsigned int max)
   {
      concurrency::wait(_random_generator()%max);
   }

private:
   // Index of the left chopstick in the chopstick array.
   chopstick& _left;
   // Index of the right chopstick in the chopstick array.
   chopstick& _right;

   // The name of the philosopher.
   wstring _name;
   // Stores the number of times the philosopher has eaten.
   overwrite_buffer<int> _times_eaten;

   // A random number generator.
   mt19937 _random_generator;
};

int wmain()
{
   // Create an array of index values for the chopsticks.
   array<chopstick, philosopher_count> chopsticks = {0, 1, 2, 3, 4};

   // Create an array of philosophers. Each pair of neighboring 
   // philosophers shares one of the chopsticks.
   array<philosopher, philosopher_count> philosophers = {
      philosopher(chopsticks[0], chopsticks[1], L"aristotle"),
      philosopher(chopsticks[1], chopsticks[2], L"descartes"),
      philosopher(chopsticks[2], chopsticks[3], L"hobbes"),
      philosopher(chopsticks[3], chopsticks[4], L"socrates"),
      philosopher(chopsticks[4], chopsticks[0], L"plato"),
   };

   // Begin the simulation.
   for_each (begin(philosophers), end(philosophers), [](philosopher& p) {
      p.start();
   });

   // Wait for each philosopher to finish and print his name and the number
   // of times he has eaten.
   for_each (begin(philosophers), end(philosophers), [](philosopher& p) {
      agent::wait(&p);
      wcout << p.name() << L" ate " << p.times_eaten() << L" times." << endl;
   });
}

Kod Derleniyor

Örnek kodu kopyalayın ve Visual Studio Project'te yapıştırın veya adlı bir dosyaya yapıştırın philosophers deadlock.cpp ve Visual Studio komut istemi penceresinde aşağıdaki komutu çalıştırın.

cl.exe /EHsc philosophers-deadlock.cpp

Top

Kilitlenme önlemek için JOIN kullanmak

Bu bölümde, ileti arabelleği ve ileti iletme işlevlerini kilitlenme olasılığını ortadan kaldırmak için nasıl kullanılacağını gösterir.

Bu daha önceki bir örneği ile ilişkili philosopher sınıfı her yerini critical_section kullanarak nesne bir concurrency::unbounded_buffer nesnesi ve bir join nesne.join Nesne gören philosopher chopsticks sağlayan bir arbiter olarak.

Bu örnek unbounded_buffer gelen bir iletiyi bir hedef aldığında çünkü sınıf bir unbounded_buffer iletiyi ileti sırasından kaldırılan nesne.Böylece bir unbounded_buffer belirtmek için bir ileti chopstick tutan nesne.Bir unbounded_buffer tutan hiçbir ileti nesnesi gösterir chopstick kullanılıyor.

Bu örnek bir olmayan-doyumsuz kullanır join her doyumsuz olmayan bir birleşim verir çünkü nesne philosopher nesne hem chopsticks erişimi yalnızca her ikisi de unbounded_buffer nesneleri içeren bir ileti.Kullanılabilir olana kadar doyumsuz bir birleşim iletileri kabul ettiğinden doyumsuz bir birleşim kilitlenmeyi önlemek değildir.Tüm doyumsuz, kilitlenme oluşabilir join nesneler iletilerinden birini alabilirsiniz, ancak sonsuza kadar diğer için kullanılabilir olmasını bekleyin.

Doyumsuz ve doyumsuz olmayan birleştirmeler ve çeşitli ileti arabelleği türleri arasındaki farklar hakkında daha fazla bilgi için bkz: Zaman uyumsuz ileti blokları.

Bu örnekte kilitlenmeyi önlemek için

  1. Aşağıdaki kod, örnekten kaldırın.

    // A shared array of critical sections. Each critical section 
    // guards access to a single chopstick.
    critical_section locks[philosopher_count];
    
  2. Türünü değiştirmek _left ve _right veri üyeleri philosopher için sınıf unbounded_buffer.

    // Message buffer for the left chopstick.
    unbounded_buffer<chopstick>& _left;
    // Message buffer for the right chopstick.
    unbounded_buffer<chopstick>& _right;
    
  3. Değiştirmek philosopher kurucusu olabilmesi için unbounded_buffer nesneleri parametrelerinin olarak.

    explicit philosopher(unbounded_buffer<chopstick>& left, 
       unbounded_buffer<chopstick>& right, const wstring& name)
       : _left(left)
       , _right(right)
       , _name(name)
       , _random_generator(42)
    {
       send(_times_eaten, 0);
    }
    
  4. Değiştirmek pickup_chopsticks yöntemi bir olmayan-doyumsuz join iletileri ileti arabellekleri için hem chopsticks almak için nesne.

    // Gains access to the chopsticks.
    vector<int> pickup_chopsticks()
    {
       // Create a non-greedy join object and link it to the left and right 
       // chopstick.
       join<chopstick, non_greedy> j(2);
       _left.link_target(&j);
       _right.link_target(&j);
    
       // Receive from the join object. This resolves the deadlock situation
       // because a non-greedy join removes the messages only when a message
       // is available from each of its sources.
       return receive(&j);
    }
    
  5. Değiştirmek putdown_chopsticks yöntemi hem chopsticks ileti arabellekleri ileti göndererek chopsticks erişim serbest bırakmak.

    // Releases the chopsticks for others.
    void putdown_chopsticks(int left, int right)
    {
       // Add the values of the messages back to the message queue.
       asend(&_left, left);
       asend(&_right, right);
    }
    
  6. Değiştirmek run sonuçları tutmak için bir yöntem pickup_chopsticks yöntemi ve bu sonuçları geçmek için putdown_chopsticks yöntemi.

    // Performs the main logic of the dining philosopher algorithm.
    void run()
    {
       // Repeat the thinks/eat cycle a set number of times.
       for (int n = 0; n < eat_count; ++n)
       {
          think();
    
          vector<int> v = pickup_chopsticks(); 
    
          eat();
    
          send(_times_eaten, n+1);
    
          putdown_chopsticks(v[0], v[1]);
       }
    
       done();
    }
    
  7. Değişiklik bildirimi chopsticks değişken wmain dizisi olarak işlev unbounded_buffer nesneleri her bir ileti tutun.

    // Create an array of message buffers to hold the chopsticks.
    array<unbounded_buffer<chopstick>, philosopher_count> chopsticks;
    
    // Send a value to each message buffer in the array.
    // The value of the message is not important. A buffer that contains
    // any message indicates that the chopstick is available.
    for_each (begin(chopsticks), end(chopsticks), 
       [](unbounded_buffer<chopstick>& c) {
          send(c, 1);
    });
    

Örnek

Dd742294.collapse_all(tr-tr,VS.110).gifDescription

Aşağıdaki kullanan tamamlanmış doyumsuz olmayan örnekler join nesneleri kilitlenme riski ortadan kaldırmak için.

Dd742294.collapse_all(tr-tr,VS.110).gifKod

// philosophers-join.cpp
// compile with: /EHsc
#include <agents.h>
#include <string>
#include <array>
#include <iostream>
#include <algorithm>
#include <random>

using namespace concurrency;
using namespace std;

// Defines a single chopstick.
typedef int chopstick;

// The total number of philosophers.
const int philosopher_count = 5;

// The number of times each philosopher should eat.
const int eat_count = 50;

// Implements the logic for a single dining philosopher.
class philosopher : public agent 
{
public:
   explicit philosopher(unbounded_buffer<chopstick>& left, 
      unbounded_buffer<chopstick>& right, const wstring& name)
      : _left(left)
      , _right(right)
      , _name(name)
      , _random_generator(42)
   {
      send(_times_eaten, 0);
   }

   // Retrieves the number of times the philosopher has eaten.
   int times_eaten()
   {
      return receive(_times_eaten);
   }

   // Retrieves the name of the philosopher.
   wstring name() const
   {
      return _name;
   }

protected:
   // Performs the main logic of the dining philosopher algorithm.
   void run()
   {
      // Repeat the thinks/eat cycle a set number of times.
      for (int n = 0; n < eat_count; ++n)
      {
         think();

         vector<int> v = pickup_chopsticks(); 

         eat();

         send(_times_eaten, n+1);

         putdown_chopsticks(v[0], v[1]);
      }

      done();
   }

   // Gains access to the chopsticks.
   vector<int> pickup_chopsticks()
   {
      // Create a non-greedy join object and link it to the left and right 
      // chopstick.
      join<chopstick, non_greedy> j(2);
      _left.link_target(&j);
      _right.link_target(&j);

      // Receive from the join object. This resolves the deadlock situation
      // because a non-greedy join removes the messages only when a message
      // is available from each of its sources.
      return receive(&j);
   }

   // Releases the chopsticks for others.
   void putdown_chopsticks(int left, int right)
   {
      // Add the values of the messages back to the message queue.
      asend(&_left, left);
      asend(&_right, right);
   }

   // Simulates thinking for a brief period of time.
   void think()
   {
      random_wait(100);
   }

   // Simulates eating for a brief period of time.
   void eat()
   {      
      random_wait(100);      
   }

private:
   // Yields the current context for a random period of time.
   void random_wait(unsigned int max)
   {
      concurrency::wait(_random_generator()%max);
   }

private:
   // Message buffer for the left chopstick.
   unbounded_buffer<chopstick>& _left;
   // Message buffer for the right chopstick.
   unbounded_buffer<chopstick>& _right;

   // The name of the philosopher.
   wstring _name;
   // Stores the number of times the philosopher has eaten.
   overwrite_buffer<int> _times_eaten;

   // A random number generator.
   mt19937 _random_generator;
};

int wmain()
{
   // Create an array of message buffers to hold the chopsticks.
   array<unbounded_buffer<chopstick>, philosopher_count> chopsticks;

   // Send a value to each message buffer in the array.
   // The value of the message is not important. A buffer that contains
   // any message indicates that the chopstick is available.
   for_each (begin(chopsticks), end(chopsticks), 
      [](unbounded_buffer<chopstick>& c) {
         send(c, 1);
   });

   // Create an array of philosophers. Each pair of neighboring 
   // philosophers shares one of the chopsticks.
   array<philosopher, philosopher_count> philosophers = {
      philosopher(chopsticks[0], chopsticks[1], L"aristotle"),
      philosopher(chopsticks[1], chopsticks[2], L"descartes"),
      philosopher(chopsticks[2], chopsticks[3], L"hobbes"),
      philosopher(chopsticks[3], chopsticks[4], L"socrates"),
      philosopher(chopsticks[4], chopsticks[0], L"plato"),
   };

   // Begin the simulation.
   for_each (begin(philosophers), end(philosophers), [](philosopher& p) {
      p.start();
   });

   // Wait for each philosopher to finish and print his name and the number
   // of times he has eaten.
   for_each (begin(philosophers), end(philosophers), [](philosopher& p) {
      agent::wait(&p);
      wcout << p.name() << L" ate " << p.times_eaten() << L" times." << endl;
   });
}

Dd742294.collapse_all(tr-tr,VS.110).gifComments

Bu örnek aşağıdaki çıktıyı üretir.

aristotle ate 50 times.
descartes ate 50 times.
hobbes ate 50 times.
socrates ate 50 times.
plato ate 50 times.

Kod Derleniyor

Örnek kodu kopyalayın ve Visual Studio Project'te yapıştırın veya adlı bir dosyaya yapıştırın philosophers join.cpp ve Visual Studio komut istemi penceresinde aşağıdaki komutu çalıştırın.

cl.exe /EHsc philosophers-join.cpp

Top

Ayrıca bkz.

Kavramlar

Zaman uyumsuz aracıları kitaplığı

Zaman uyumsuz aracıları

Zaman uyumsuz ileti blokları

İleti gönderme fonksiyonları

Eşitleme veri yapıları

Diğer Kaynaklar

Eşzamanlılık çalışma zamanı izlenecek yollar