Cet article a fait l'objet d'une traduction automatique.
Affaires simultanées
Résoudre le problème philosophes dîner avec agents asynchrones
Rick Molloy
Cet article est basé sur une version préliminaire de Visual C++ 2010. Toutes les informations sont sujets à modification.
Téléchargement de code disponible de la bibliothèque de code MSDN
Parcourir le code en ligne
Contenu
Les philosophes dîner
Une approche basée sur l'acteur
Une solution de cinq classes
Blocs de message et les messages
Agents et le bloc de message jointure
Tester le Philosopher et Affichage état
L'implémentation de la classe de table
Heure de service
Permettant aux développeurs C++ de l'écriture très applications simultanées est une vue principale de Visual Studio 2010. La version bêta comprend les exécution simultanéité, bibliothèques parallèles et destinées à résoudre plusieurs problèmes courants relatifs aux développeurs empêche de déverrouiller le potentiel de performances inhérent à matériel multinoyau des outils de développement. Notamment, cela inclut s'assurer que les développeurs peuvent identifier et tirer parti des opportunités de simultanéité d'accès aux données dans leur code, manière productive gérer état partagé et ses effets secondaires et ne pas avoir à se soucier construction infrastructure faible surcharge simultanéité évolutive lors de l'exécution sur divers de matériel.
Dans cet article, je vais expliquer comment utiliser la nouvelle bibliothèque agents asynchrone incluses comme partie de Visual C++ 2010 pour gérer les problèmes qui peuvent survenir avec état partagé. Pour vous montrer comment cela fonctionne, je Guide une implémentation d'un problème d'accès concurrentiel classique : philosophes dîner de Djikstra. Vous verrez comment la construction de programmation basé l'acteur d'un agent en combinaison avec API passage de messages asynchrone peut servir à fournir un correct et facile à comprendre la solution à ce problème ne repose pas directement sur des primitives de thread ou la synchronisation.
Les philosophes dîner
Pour ceux qui n'êtes pas familier avec elle, le problème dîner des philosophes est conçu pour illustrer la complexité de gestion d'état partagé dans un environnement multithread. Voici le problème :
À une table arrondi asseoir cinq philosophes qui alterner entre penser et manger à partir d'une grande salade de riz au hasard intervalles. Malheureusement pour les philosophes sont seulement cinq chopsticks à la table (voir figure 1 ), et avant que les philosophes peuvent commencer à manger, ils doivent acquérir une paire de chopsticks. Étant donné que les chopsticks sont partagés entre les philosophes, accès aux chopsticks doit être protégé, mais si soin n'est pas pris, simultanéité d'accès aux données des problèmes surviennent. Surtout, insuffisance (point prévu) via le blocage peut se produire : si chaque philosopher prélève un chopstick dès qu'il est disponible et attend indéfiniment l'autre, finalement qu'ils sont tous finir maintenant qu'un seul chopstick avec aucun risque d'acquérir une autre.
Figure 1 les philosophes et Chopsticks
Le problème dîner des philosophes est généralement représenté dans le code par un thread pour chaque philosopher et une forme d'état partagé utilisé pour représenter chaque les chopsticks. Solutions simples à ce problème impliquent souvent introduire une entité de serveur pour coordonner l'accès aux chopsticks présentation verrou classement heuristiques et le travail manuellement avec threads API, les sections critiques, sémaphores ou moniteurs pour gérer l'état.
La plupart des développeurs sont cohérents que création correcte, verrouillage importante est considéré comme difficile et fragile au mieux. Par conséquent, la plupart des implémentations de ce problème dîner des philosophes tendant à avoir divulguée dans les détails d'implémentation. Par exemple, les philosophes peuvent être conscient des primitives de synchronisation ou de chopsticks de ses voisins. Cela devient particulièrement problématique si vous souhaitez generalize le problème à un nombre arbitraire de philosophes ou refocus une implémentation sur le domaine qui pose problème, tels que ce que fait chaque philosopher, plutôt que de se concentrer sur la synchronisation et de threads primitives.
Une approche basée sur l'acteur
Nous allons étudier un modèle qui est basé sur la bibliothèque d'agents asynchrone. Dîner des philosophes réellement seulement possède deux sections modérément difficiles : créer un thread pour chaque philosopher pour s'exécuter dans et coordonner accès des philosophes aux chopsticks. La bibliothèque d'agents asynchrone fournit un modèle de programmation basé l'acteur et passage API de messages asynchrone, et vous devez deux dans l'implémentation.
Dans la bibliothèque agents asynchrone, la classe agent modèles un motif d'acteur et fournit une méthode d'exécution asynchrone. J'utiliser un agent pour chaque philosopher et tirer parti de la méthode exécution pour répondre à la demande de chaque philosopher en son propre thread.
La seconde partie d'une solution correcte implique la gestion des accès simultanés aux chopsticks et Contrairement aux solutions traditionnelles utilisant sémaphores ou verrous, je vais utiliser le passage des API dans la bibliothèque agents asynchrone de messages pour déplacer les chopsticks entre les mains des philosophes et la table.
Comme chaque philosopher transitions entre penser et manger, il prélève les chopsticks et les renvoie à la table en envoyant des messages. Vous verrez que plus à fournir une abstraction au-dessus de threads et état partagé, cela permet également l'implémentation de rester plus axé sur le domaine de ce problème à activer d'autres solutions.
Une solution de cinq classes
La conception de base va utiliser cinq types :
- Une classe Chopstick qui est relativement auto-évaluation explicatif.
- Une classe ChopstickProvider qui sera utilisée pour maintenez le chopsticks sur la table et les fournir aux philosophes.
- Une classe Philosopher qui est responsable de penser et manger et est conscient seulement de ses ChopstickProviders deux.
- Énumération PhilospherState qui peut être penser ou manger.
- Une classe de la table qui contient un ensemble de philosophes et un ensemble de Chopsticks. Table est responsable de la définition de la table à placer un Chopstick unique dans un ChopstickProvider entre chaque Philosopher. la figure 2 présente les relations entre les classes.
La figure 2 classes utilisées dans la solution philosophes dîner
Commençons par la classe plus simple : Chopstick. Le le Chopstick vise à simplement existent comme un chopstick et pouvoir identifier. Par conséquent, l'implémentation est une classe simple qui comporte une variable membre identificateur, un constructeur qui initialise l'identificateur et une méthode pour renvoyer l'identificateur.
class Chopstick{
const std::string m_Id;
public:
Chopstick(std::string && Id):m_Id(Id){};
const std::string GetID() {
return m_Id;
};
};
Il est important de noter que la m_Id champ identificateur est une variable membre const. Je ne veux pas la chopstick pour que tout état de son propre peut obtenir modifiée accidentellement par un autre thread. Également remarquer que le constructeur est utilise une référence r-value dans sa liste de paramètres (le et). C'est une structure de langue nouvelle dans Visual Studio 2010 dont fait partie le brouillon C ++ 0 x standard. Une référence r-value ici permet au compilateur de éviter un appel de constructeur de copie et de déplacer ID vers m_Id dans l'instance Chopstick Si ID est une variable temporaire ou r-value.
Blocs de message et les messages
ChopstickProvider est également simple. Il peut être considéré comme un tampon de stockage dont l'objectif consiste à accepter Chopsticks lors de leur envoi au et à fournir Chopsticks à un Philosopher à la demande.
Voici le premier endroit où j'utilise l'API d'agent. J'utilise ce qui est connu comme un bloc de message pour le ChopstickProvider. Un bloc de message est défini comme une classe qui est capable d'envoyer et recevoir des messages. Plus spécifiquement, si vous êtes puisse envoyer un message à message block, le bloc est appelé une cible et, si vous êtes capable de recevoir un message à partir d'un bloc de message, il est appelé une source de.
Dans cet exemple, je devrai le ChopstickProvider est à la fois une source d'et une cible car les philosophes va être les recevoir chopsticks à partir de la ChopstickProviders lorsque les philosophes sont hungry et les envoyer vers le ChopstickProviders lorsque les philosophes êtes prêts à penser à nouveau. Voici un exemple de mettre les travaux :
//create a chopstick
Chopstick chopstick("chopstick one");
//create a ChopstickProvider to store the chopstick
ChopstickProvider chopstickBuffer;
//put the chopstick in the chopstick holder
Concurrency::send(chopstickBuffer, &chopstick);
//request a chopstick from the chopstick buffer
Chopstick* result = Concurrency::receive(chopstickBuffer);
Vous constatez que, après la déclaration un chopstick et un chopstickBuffer (une instance de la ChopstickProvider toujours non définie), Concurrency::send permet de placer l'adresse de chopstick dans le ChopstickBuffer et Concurrency::receive retourne un pointeur vers un Chopstick à partir de la chopstickBuffer. Concurrency::Send est une méthode de modèle qui synchrone place un message dans un bloc de message. Concurrency::Receive est une méthode de modèle qui renvoie un message à partir d'un bloc de message. Blocs Concurrency::Receive jusqu'à ce qu'un message soit disponible dans le bloc de message.
La bibliothèque d'agents asynchrone fournit également Concurrency::asend et Concurrency::try_receive. Concurrency::asend asynchrone génère une tâche à envoyer le message sans attendre l'accusé de réception de réception. Concurrency::try_receive est un appel non blocage que les s'acquièrent un message si elle est disponible.
Maintenant Commençons par définir en fait le ChopstickProvider. Vous souvenez-vous que J'AI dit qu'il était simple ? C'est un typedef :
typedef Concurrency::unbounded_buffer<Chopstick*>
ChopstickProvider;
La bibliothèque agents fournit un bloc de message appelé le unbounded_buffer dont les fonctionnalités dont vous avez besoin. Un unbounded_buffer est une classe modèle qui est une source et cible. Il stocke un nombre limité de mémoire de messages dans une file d'attente interne. Messages sont supprimés de la file d'attente lorsqu'ils sont demandés. Vous n'avez pas la qualité illimitée dans cette implémentation de dîner des philosophes car il existe un nombre limité de chopsticks va parcourir chaque détenteur chopstick. La fonctionnalité clée que vous intéresse dans la unbounded_buffer est sa sémantique de déplacement.
Agents et le bloc de message jointure
Maintenant jetons un coup à l'implémentation de la classe Philosopher. Ceci est la partie intéressante du problème dîner des philosophes, où vous vérifiez que chaque philosopher s'exécute sur son propre thread et qu'Accès à la ressource partagée est coordonnée) est correctement. La classe Philosopher utilise deux structures supplémentaires à partir de la bibliothèque d'agents asynchrone, la classe abstraite agent et l'instruction de jointure. Tout d'abord que je vais décrire l'agent et son utilisation.
La classe agent est une classe abstraite qui s'apparente à ce qui est parfois appelé un acteur motif ou programmation basé l'acteur. Agents sont destinés à être utilisés pour faciliter la suppression des dépendances et partagé état entre les composants actifs d'une application en laissant les agents encapsuler état et communiquer avec eux uniquement via passage sur un petit ensemble d'interfaces publiques de messages. C'est plus compliquée qu'il est, mais j'espère que vous pouvez voir les ressemblances à la classe Philosopher.
Comme je L'AI mentionné précédemment, la classe Philosopher a besoin doit être exécuté sur son propre thread. Cette conversion à la terminologie des agents, que cela signifie qu'il doit d'être une tâche active, donc Philosopher va dérivés Concurrency::agent publiquement. La classe Philosopher doit également être conscient de deux ChopstickProviders (une pour chaque disponible) et puis les utiliser pour une transition avec succès entre penser et manger. La classe Philosopher n'est pas encore au complet, mais vous verrez comment cela commence à traduire en un synopsis de classe :
enum PhilosopherState {Thinking,Eating};
typedef Concurrency::unbounded_buffer<Chopstick*> ChopstickProvider;
class Philosopher : public Concurrency::agent {
ChopstickProvider* m_LeftChopstickProvider;
ChopstickProvider* m_RightChopstickProvider;
public:
const std::string m_Name;
const int m_Bites;
Philosopher(const std::string&& name,
int bites=500):m_Name(name),
m_Bites(bites){};
...
}
Philosopher a un pointeur vers deux ChopstickProviders, un nom, le nombre de bites ou active la Philosopher prendra, et un constructeur pour l'initialisation.
Ce qui est absent, cependant, est un moyen pour initialiser la ChopstickProviders dans l'agent. Vous ne souhaitez pas les initialiser au moment de création, car les philosophes sont indépendants des Chopsticks et leur ChopstickProviders. Vous pourriez créer une méthode publique supplémentaire comme AssignChopsticks (ChopstickProvider * gauche, droite ChopstickProvider *), mais ensuite, vous devrez prendre une attention, ou un verrou, pour vous assurer que cette méthode est thread-safe.
Au lieu de cela, je vais créer une interface publique afin que le ChopstickProviders peuvent être affectés de façon asynchrone. J'AI ce faire, Ajout de deux variables de membres unbounded_buffer plus publics et il permet d'envoyer les ChopstickProviders deux philosophes :
Concurrency::unbounded_buffer<ChopstickProvider*>
LeftChopstickProviderBuffer;
Concurrency::unbounded_buffer<ChopstickProvider*>
RightChopstickProviderBuffer;
Je suis désormais prêt à commencer à implémenter la méthode Philosopher qui s'exécutent dans son propre thread, attendez le ChopstickProviders soit initialisé, puis démarrez alternant entre penser et manger. J'implémenter la méthode virtuelle exécution de la classe de base agent. Agent::Run sera démarrée dans sa propre tâche lorsqu'un appel à agent::start est effectué.
void run() {
La première chose que J'AI besoin s'exécutent dans est initialiser la ChopstickProviders en attente de messages soient envoyés sur les méthodes publiques avec réception :
//initialize the ChopstickProviders
m_LeftChopstickProvider =
Concurrency::receive(LeftChopstickProviderBuffer);
m_RightChopstickProvider =
Concurrency::receive(RightChopstickProviderBuffer);
Maintenant je dois transition entre penser et manger. Pour cela je vais utiliser deux méthodes supplémentaires, PickupChopsticks et PutDownChopsticks :
for(int i = 0; i < m_Bites;++i) {
Think();
std::vector<Chopstick*> chopsticks(PickupChopsticks());
Eat();
PutDownChopsticks(chopsticks);
}
La seule chose à implémenter dans la méthode exécuter consiste à nettoyer en renvoyant le ChopstickProviders de manière à ce qu'ils puissent être réutilisées si nécessaire et pour définir l'état d'agent à effectuées.
Concurrency::send(LeftChopstickProviderBuffer,
m_LeftChopstickProvider);
Concurrency::send(RightChopstickProviderBuffer,
m_RightChopstickProvider);
this->done(Concurrency::agent_done);
}
Poursuivre, les méthodes supplémentaires Eat et Think sont simples. Dans le problème d'origine qu'ils sont décrits comme boucles toupie aléatoire :
private:
void Eat() {
RandomSpin();
};
void Think() {
RandomSpin();
};
};
L'implémentation PickupChopsticks est plus intéressant, car il gère l'acquisition et au relâchement les chopsticks sans introduire de blocages ou conditions de concurrence. Pour implémenter cette je vais utiliser un autre bloc de messagerie de la bibliothèque agents asynchrone appelée jointure. Concurrency::JOIN est un bloc de message qui attend les messages provenant de plusieurs sources, potentiellement de types différents. Une fois que tous les messages ont été reçus, il génère un message conglomérat. Une jointure peut prendre en charge sources de la même ou plusieurs types et acquérir des messages d'une manière gourmands ou non gourmands. Cela signifie qu'il existe quatre variantes de jointure dans la bibliothèque agents. La classe Philosopher va utiliser une jointure non gourmands type seule. la figure 3 illustre un exemple simple d'une jointure dans le code.
La figure 3 A simple jointure
//create two chopsticks
Chopstick chopstick1("chopstick one");
Chopstick chopstick2("chopstick two");
//create ChopstickProviders to store the chopstick
ChopstickProvider chopstickBuffer1;
ChopstickProvider chopstickBuffer2;
//put a chopstick in each chopstick holder
Concurrency::send(chopstickBuffer1, &chopstick1);
Concurrency::send(chopstickBuffer2, &chopstick2);
//declare a single-type non greedy join to acquire them.
//the constructor parameter is the number of inputs
Concurrency::join<Chopstick*,non_greedy> j(2);
//connect the chopstick providers to the join so that messages
//sent will propagate forwards
chopstickBuffer1.link_target(&j);
chopstickBuffer2.link_target(&j);
//the single type join message block produces a vector of Chopsticks
std::vector<Chopstick*> result = Concurrency::receive(j);
Maintenant je suis prêt à implémenter PickupChopsticks. La première chose à noter est que vous renvoyer un ensemble de chopsticks en retournant un vecteur. Pour obtenir une paire de chopsticks dans les moyens de fournisseur chopstick à l'aide d'une jointure non gourmands. La variante non gourmands de jointure attend une offre des messages de toutes les sources liées et ensuite, une fois qu'une offre de chaque source confirme qu'il peut réellement prendre possession du message. C'est ce qui empêche blocage dans cet exemple. Si J'AI a utilisé gourmands en tant que paramètre modèle pour la jointure, le message est provenir dès une offre est effectuée et plus tôt ou tard chaque Philosopher aurait un chopstick unique et ils serait tout être verrouillés.
Voici le code de PickupChopsticks. J'AI créer une jointure, la liaison il aux fournisseurs chopstick et attendre les chopsticks arrivée :
std::vector<Chopstick*> PickupChopsticks() {
//create the join
Concurrency::join<Chopstick*,Concurrency::non_greedy> j(2);
m_LeftChopstickProvider->link_target(&j);
m_RightChopstickProvider->link_target(&j);
//pickup the chopsticks
return Concurrency::receive (j);
}
L'implémentation PutDownChopsticks est aussi simple. Tout ce dont J'AI avez besoin pour faire est renvoyer les chopsticks que j'ai acquises du vecteur à l'aide la asend méthode asynchrone :
void PutDownChopsticks(std::vector<Chopstick*>& v) {
Concurrency::asend(m_LeftChopstickProvider,v[0]);
Concurrency::asend(m_RightChopstickProvider,v[1]);
};
Tester le Philosopher et Affichage état
La classe Philosopher peut être utilisée comme, mais vous devrez comprendre comment démarrer la classe Philosopher et demander un moyen d'un rapport sur son état ; si elle est manger ou penser.
Pour démarrer le philosopher, appelez la méthode agent::start, qui génère une tâche qui appelle la méthode d'exécution virtuelle. Pour signaler l'état, J'AI introduire deux blocs message plus à partir de la bibliothèque agents : Concurrency::overwrite_buffer et Concurrency::call.
Concurrency::overwite_buffer <t> est un bloc de message qui stocke une valeur unique qui peut être remplacée et il génère une copie de la valeur lorsqu'il est demandé. Comme avec les autres blocs de message, overwrite_buffer peuvent être liés aux autres objectifs et la propagation de message se produit dans l'ordre. Je vais ajouter une variable membre public à la classe Philosopher qui est un overwrite_buffer <philosopherstate> et mettre à jour en envoyant des messages au en tant que les transitions Philosopher à partir de souhaitez à Eating. Plus précisément, cela impliquer Ajout d'une variable membre à la classe Philosopher :
Concurrency::overwrite_buffer<PhilosopherState> CurrentState;
Cela signifie également modification Eat et Think pour mettre à jour d'état :
void Eat() {
send(&CurrentState,PhilosopherState::Eating);
RandomSpin();
};
void Think() {
send(&CurrentState,PhilosopherState::Thinking);
RandomSpin();
};
Concurrency::Call <t> est un bloc de message qui est construit avec un functor et génère une tâche à exécuter le functor lorsque qu'il reçoit le message. Vous pouvez utiliser l'appel en combinaison avec le overwrite_buffer nouvellement défini sur la Philosopher établir un rapport sur son état, comme illustré figure 4 .
La figure 4 rapport Philosopher état
//create an instance of a Philosopher and have it take 5 bites
Philosopher Descartes("Descartres",5);
//create a pair of chopsticks
Chopstick chopstick1("chopstick1");
Chopstick chopstick2("chopstick2");
//create a pair of ChopstickProviders
ChopstickProvider chopstickBuffer1;
ChopstickProvider chopstickBuffer2;
//associate our chopstick providers with Descartes
Concurrency::send(&Descartes.LeftChopstickProvider, chopstickBuffer1);
Concurrency::send(&Descartes.RightChopstickProvider, chopstickBuffer2);
//start the Philosopher asynchronously
agent::start(&Descartes);
//declare a call that will be used to display the philosophers state
//note this is using a C++0x lambda
Concurrency::call<PhilosopherState> display([](PhilosopherState state){
if (state == Eating)
std::cout << "eating\n" ;
else
std::cout << "thinking\n" ;
});
//link the call to the status buffer on our Philosopher
Descartes.CurrentState .link_target (&display);
//start our Philosopher eating / thinking by sending chopsticks
asend(&chopstickBuffer1,&chopstick1);
asend(&chopstickBuffer2,&chopstick2);
Et il agit de la classe Philosopher.
L'implémentation de la classe de table
Vous avez déjà vu que tous les éléments nécessaires pour créer la classe de table. Comme nous l'avons vu précédemment, elle contient un ensemble de philosophes, un ensemble de Chopsticks et un ensemble de ChopstickProviders. La table sera responsable de salle les philosophes à la table, en plaçant un ChopstickProvider entre chacune d'elles et en plaçant un Chopstick dans chacune du ChopstickProviders. Pour une souplesse j'ai choisi pour le rendre une classe de modèle, et qu'il contient une référence à une liste de philosophes, un vecteur de Chopsticks et un vecteur de détenteurs de Chopstick.
template<class PhilosopherList>
class Table {
PhilosopherList & m_Philosophers;
std::vector<ChopstickProvider*> m_ChopstickProviders;
std::vector<Chopstick*> m_Chopsticks;
...
La méthode publique uniquement dans la classe table est le constructeur, qui initialise les vecteurs et affecte ChopstickProviders à chaque Philosopher (voir La figure 5 ).
La figure 5 table constructeur
Table(PhilosopherList& philosophers): m_Philosophers(philosophers) {
//fill the chopstick and the chopstick holder vectors
for(size_t i = 0; i < m_Philosophers.size();++i) {
m_ChopstickProviders.push_back(new ChopstickProvider());
m_Chopsticks.push_back(new Chopstick("chopstick"));
//put the chopsticks in the chopstick providers
send(m_ChopstickProviders[i],m_Chopsticks[i]);
}
//assign the philosophers to their spots
for(size_t leftIndex = 0;
leftIndex < m_Philosophers.size();++leftIndex) {
//calculate the rightIndex
int rightIndex = (leftIndex+1)% m_Philosophers.size();
//send the left & right chopstick provider to the appropriate Philosopher
Concurrency::asend(& m_Philosophers[leftIndex].LeftChopstickProviderBuffer,
m_ChopstickProviders[leftIndex]);
Concurrency::asend(& m_Philosophers[leftIndex].RightChopstickProviderBuffer,
m_ChopstickProviders[rightIndex]);
}
}
~Table(){
m_ChopstickProviders.clear();
m_Chopsticks.clear();
}
Heure de service
Enfin, pour implémenter principal, J'AI devez déclarer la liste des philosophes, créer une table, démarrer les philosophes, raccorder un mécanisme de création de rapports et attendre les manger (voir La figure 6 ).
La figure 6 Mealtime
int main() {
//create a set of Philosophers using the tr1 array
std::tr1::array<Philosopher,5> philosophers =
{"Socrates", "Descartes", "Nietzsche", "Sartre", "Amdahl"};
Table<std::tr1::array<Philosopher,5>> Table(philosophers);
//create a list of call blocks to display
std::vector<Concurrency::call<PhilosopherState>*> displays;
//start the Philosophers and create display item
std::for_each(
philosophers.begin(),philosophers.end(),[&displays](Philosopher& cur) {
//create a new call block to display the status
Concurrency::call<PhilosopherState>* consoleDisplayBlock =
new Concurrency::call<PhilosopherState>([&](PhilosopherState in){
//cout isn't threadsafe between each output
if(in == Eating)
std::cout << cur.m_Name << " is eating\n";
else
std::cout << cur.m_Name << " is thinking\n";
});
//link up the display and store it in a vector
cur.CurrentState.link_target(consoleDisplayBlock);
displays.push_back(consoleDisplayBlock);
//and start the agent
cur.start();
});
//wait for them to complete
std::for_each(
philosophers.begin(),philosophers.end(),[](Philosopher& cur) {
cur.wait(&cur);
});
displays.clear();
return 1;
};
Bien entendu, il est clair comment la classe agent et le message asynchrone en transmettant que la bibliothèque d'agents asynchrone fournit peuvent vous aider éviter les difficultés de coordonner les accès à l'état partagé. J'ai géré implémenter le problème dîner des philosophes sans utiliser un verrou explicite unique et sans avoir à appeler directement les API de thread. J'ai également gérés conserver l'implémentation dans les conditions de domaine, en particulier, j'ai passé temps manipuler Chopsticks, philosophes et les tables par opposition à des tableaux d'entiers et les sémaphores.
Ce que je n'est encore est ajouté toute évolutivité inhérente aux philosophes. Cette application comme écrit n'exécuter beaucoup plus rapide sur les noyaux plus de quatre. Mais cela peut être résolus en remplaçant RandomSpin par un élément plus significatif, comme un algorithme implémenté avec l'aide de la bibliothèque parallèle modèle (PPL) le parallélisme basée sur les tâches. Je vous encourage à en savoir plus sur ce sur leblog de concurrence natif, où j'ai le code source pour cet article, mais où j'ai également ajouté quelques algorithmes affinées penser parallèle implémentées pour démontrer composability entre le PPL et la bibliothèque agents. J'ai également utilisé des fonctions de gestion des ressources l'exécution de simultanéité pour gérer la qualité de service les problèmes qui peuvent survenir avec une multitude de travail parallèle. Lorsque des ressources de traitement récupérez rare, je pouvez Assurez-vous que les philosophes toujours sont peut obtenir suffisamment pour manger.
Veuillez envoyer vos questions et commentaires àmmsync@Microsoft.com.
Rick Molloy est responsable de programme dans l'équipe Parallel plate-forme informatique chez Microsoft.