Sdílet prostřednictvím


Názorný postup: Pomocí spojení zabránit zablokování

Toto téma pomocí stravování philosophers problém použití concurrency::join třídy, aby nedošlo k zablokování aplikace.Softwarové aplikace v zablokování dojde dvěma nebo více procesy každý držet zdroje a vzájemně počkejte na uvolnění některých jiných prostředků jiného procesu.

Stravování philosophers problému je konkrétní příklad sady obecné problémy, které mohou nastat při nastavení prostředků je sdílena mezi více souběžných procesů.

Požadavky

Před zahájením tohoto postupu naleznete v následujících tématech:

Oddíly

Tento návod obsahuje následující oddíly:

  • Stravování Philosophers problém

  • Provádění Naïve

  • Pomocí spojení zabránit zablokování

Stravování Philosophers problém

Stravování philosophers problém ilustruje, jak dojde k zablokování aplikace.Tento problém v pěti philosophers druhému kulatý stůl.Každý filosofovi střídavě myslím a jíst.Každý filosofovi musí sdílet chopstick s soused vlevo a jinou chopstick s sousední vpravo.Následující obrázek znázorňuje toto rozložení.

Problém obědvajících filosofů

Jíst, musí filosofovi podržíte dvou chopsticks.Filosofovi, každý obsahuje právě jeden chopstick a čeká na jiný, pak můžete nejedí žádné filosofovi a všechny starve.

Top

Provádění Naïve

Následující příklad ukazuje naïve provádění stravování philosophers problém.philosopher Třídy, která se odvozuje z concurrency::agent, umožňuje každému filosofovi jednat nezávisle.V příkladu je sdílená pole concurrency::critical_section objekty pro každého philosopher objektu výhradní přístup k dvojici chopsticks.

K provádění na obrázku se vztahují philosopher třída představuje jeden filosofovi.int Proměnná představuje každý chopstick.critical_section Slouží jako držitelé, na které chopsticks umístěte objekty.run Metoda simuluje život filosofovi.think Metoda simuluje aktu myšlení a eat metoda simuluje aktu jíst.

A philosopher objekt uzamkne i critical_section objekty simulovat odebrání chopsticks od držitelů před volání eat metoda.Po volání eat, philosopher objektu vrací chopsticks držitelům nastavením critical_section objekty zpět do státu odemknuté.

pickup_chopsticks Metoda ukazuje, kde může dojít k zablokování.Pokud každý philosopher objektu získá přístup k jedné zámků a č philosopher objektu můžete pokračovat, protože ostatní lock je řízena jinou philosopher objektu.

Příklad

Dd742294.collapse_all(cs-cz,VS.110).gifKód

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

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 philosophers deadlock.cpp a spusťte následující příkaz v okně příkazového řádku Visual Studio.

cl.exe /EHsc philosophers-deadlock.cpp

Top

Pomocí spojení zabránit zablokování

Tato část zobrazuje způsob použití funkce předávání zpráv a zprávy vyrovnávacích pamětí vyloučit možnost zablokování.

Relace mezi starší jeden příklad philosopher třídy nahradí každý critical_section pomocí objektu concurrency::unbounded_buffer objektu a join objektu.join Objekt slouží jako arbiter poskytující chopsticks filosofovi.

V tomto příkladu unbounded_buffer třídy, protože jakmile obdrží cílovou zprávu z unbounded_buffer objekt zpráva je odebrána z fronty zpráv.Díky tomu unbounded_buffer objekt, který uchovává chopstick zpráva označuje, zda je k dispozici.unbounded_buffer Označuje objekt, který uchovává žádná zpráva je používán chopstick.

V tomto příkladu non samec join objektu, protože spojení samec dává každý philosopher objektu přístup k oběma chopsticks pouze tehdy, když obě unbounded_buffer zpráva obsahovat objekty.Samec spojení by bránit zablokování, protože samec spojení přijímá zprávy, jakmile budou k dispozici.Zablokování může dojít, pokud všechny samec join objekty jedné zprávy přijímat, ale čekat stále k dispozici jiné.

Další informace o spojeních samec a samec a rozdíly mezi různými typy zpráv vyrovnávací paměti viz Asynchronní bloků zprávy.

Aby nedošlo k zablokování v tomto příkladu

  1. Následující kód odeberte z příkladu.

    // A shared array of critical sections. Each critical section 
    // guards access to a single chopstick.
    critical_section locks[philosopher_count];
    
  2. Změna typu _left a _right data členů philosopher třídy na unbounded_buffer.

    // Message buffer for the left chopstick.
    unbounded_buffer<chopstick>& _left;
    // Message buffer for the right chopstick.
    unbounded_buffer<chopstick>& _right;
    
  3. Změnit philosopher vzít konstruktoru unbounded_buffer objekty jako jeho parametry.

    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. Změnit pickup_chopsticks non samec metodu join přijímat zprávy z vyrovnávací paměti zprávu pro oba chopsticks objektu.

    // 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. Změnit putdown_chopsticks metody uvolnění odesláním zprávy do vyrovnávací paměti zprávu pro oba chopsticks přístup chopsticks.

    // 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. Změnit run metoda pro uložení výsledků pickup_chopsticks metoda a předávat tyto výsledky putdown_chopsticks metoda.

    // 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. Upravte prohlášení chopsticks proměnné v wmain funkce být matice unbounded_buffer objekty každé podržte klávesu jednu zprávu.

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

Příklad

Dd742294.collapse_all(cs-cz,VS.110).gifDescription

Následující zobrazí vyplněný příklad, který používá jiné samec join objektů k vyloučení nebezpečí zablokování.

Dd742294.collapse_all(cs-cz,VS.110).gifKód

// 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(cs-cz,VS.110).gifKomentáře

Tento příklad vytvoří následující výstup.

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

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 philosophers join.cpp a spusťte následující příkaz v okně příkazového řádku Visual Studio.

cl.exe /EHsc philosophers-join.cpp

Top

Viz také

Koncepty

Asynchronní agenti knihovny

Asynchronní agenti

Asynchronní bloků zprávy

Funkce předávání zpráv

Synchronizace struktury dat

Další zdroje

Souběžnost Runtime návody