Este artículo proviene de un motor de traducción automática.

Operaciones simultáneas

Para resolver el problema de los filósofos cena con agentes asincrónicos

Rick Molloy

Este artículo se basa en una versión preliminar de 2010 de Visual C++. Toda la información está sujeta a cambios.

Descarga de código de la Galería de código de MSDN
Examinar el código en línea

Contenido

Los filósofos cena
Un enfoque basado en el actor
Una solución de cinco clases
Bloques de mensajes Y mensajes
Los agentes Y el bloque de mensajes combinación
Probar la Filósofa Y mostrar el estado
Implementación de la clase de tabla
Hora de comer

Habilitación de C++ a los desarrolladores escribir muy aplicaciones simultáneas es un objetivo principal de 2010 de Visual Studio. La versión beta incluye el tiempo de ejecución de simultaneidad, paralelas bibliotecas y herramientas de desarrollo destinadas a resolver varios problemas comunes que impide que los desarrolladores desbloquea el potencial de rendimiento inherente a hardware multinúcleo. En concreto, esto incluye garantizar que los desarrolladores pueden identificar y aprovechar las oportunidades para la simultaneidad en su código, administrar de forma productiva estado compartido y sus efectos secundarios y no tener que preocuparse sobre creación de infraestructura de simultaneidad de baja sobrecarga que es escalable en tiempo de ejecución en una amplia gama de hardware.

En este artículo, voy a Demuestre cómo utilizar la nueva biblioteca de agentes asincrónico incluido como parte de 2010 de Visual C++ para administrar las dificultades que pueden surgir con el estado compartido. Para mostrarle cómo funciona, guiará a través de una implementación de un problema de simultaneidad clásico: Dining filósofos del Djikstra. Verá cómo se puede utilizar la construcción de programación de actor-en función de un agente en combinación con las API de paso de mensajes asincrónicas para proporcionar una correcta y fácil de entender la solución a este problema que no dependen directamente de subprocesos o sincronización primitivos.

Los filósofos cena

Para aquellos que no está familiarizado con ella, el problema Dining filósofos sirve para ilustrar las complejidades de administración de estado compartido en un entorno multiproceso. Éste es el problema:

En una tabla de sistema están los filósofos cinco que alternar entre pensando en y comer en un Tarro grande de rice al azar intervalos. Desgraciadamente para los filósofos hay chopsticks sólo cinco en la tabla (consulte la figura 1 ), y antes de los filósofos pueden comenzar a comer, debe adquirir un par de chopsticks. Puesto que la chopsticks se comparten entre los filósofos, acceso a las chopsticks debe estar protegido, pero si no se toman cuidado, surgir problemas de simultaneidad. En concreto, puede producirse inanición (pun diseñada) a través de un interbloqueo: si cada filósofa toma un chopstick tan pronto como está disponible y espera indefinidamente al otro, finalmente todos terminará hasta que contiene sólo un chopstick con ninguna posibilidad de adquirir otra.

fig01.gif

Figura 1 el filósofos y Chopsticks

El problema Dining filósofos suele representarse en el código por un subproceso para cada filósofa y algún tipo de estado compartido utilizado para representar cada uno de los chopsticks. Soluciones sencillas para este problema a menudo implican Introducción a una entidad camarero para coordinar el acceso a las chopsticks, introducción a orden heurística y trabajar manualmente con subprocesos API, las secciones críticas, semáforos o monitores administre el estado de bloqueo.

La mayoría de los desarrolladores se acuerdo que generar correcto, bloqueo realista se considera un reto y frágil al mejor. Como resultado, la mayoría de implementaciones del problema Dining filósofos suelen tener detalles de implementación pérdidos en ellos. Por ejemplo, los filósofos pueden ser conscientes de las primitivas de sincronización o de chopsticks de sus vecinos. Esto resulta especialmente problemático si desea generalize problema a un número arbitrario de filósofos o refocus una implementación en el dominio del problema, como lo que hace cada filósofa, en lugar de centrarse en la sincronización y primitivas de subprocesos.

Un enfoque basado en el actor

Recorra vamos a un diseño que se basa en la biblioteca de agentes asincrónica. Dining los filósofos realmente sólo tiene dos secciones moderadamente difíciles: crear un subproceso para cada filósofa para ejecutarse en y coordinar el acceso los filósofos a las chopsticks. La biblioteca de agentes asincrónica proporciona un modelo de programación en función de actor y mensaje asincrónico pasando las API, y necesitará ambos en la implementación.

En la biblioteca agentes asincrónica, la clase de agente modela un modelo de actor y proporciona un método de ejecución asincrónico. Utilizar un agente para cada filósofa y aprovechar el método de ejecución para cumplir los requisitos de cada filósofa que se ejecutan en su propio subproceso.

La segunda parte de una solución correcta implica la administración acceso simultáneo a las chopsticks y, en contraste con soluciones tradicionales mediante semáforos o bloqueos, va usar el mensaje, pasando las API en la biblioteca de agentes asincrónica para mover los chopsticks entre manos de los filósofos y la tabla.

Como cada filósofa transiciones entre el pensamiento y comer, se reanudará los chopsticks y los devuelve a la tabla mediante el envío de mensajes. Verá que así como proporcionar algunos abstracción en la parte superior de subprocesos y estado compartido, esto también permitirá la implementación para permanecer más centrado en el dominio de los problemas que permiten otras soluciones.

Una solución de cinco clases

El diseño básico se va a utilizar cinco tipos:

  • Una clase Chopstick que es relativamente automática explicativo.
  • Una clase ChopstickProvider que se utilizará para mantenga los chopsticks en la tabla y a proporcionar a los filósofos.
  • Una clase Filósofa que es responsable de pensamiento y comer y es consciente sólo de sus ChopstickProviders dos.
  • Una enumeración PhilospherState que puede pensando en o comer.
  • Una clase de tabla que contiene un conjunto de filósofos y un conjunto de Chopsticks. Tabla es responsable de la configuración de la tabla colocando un Chopstick único en un ChopstickProvider entre cada Filósofa. la figura 2 muestra las relaciones entre las clases.

fig02.gif

La Figura 2 clases utilizadas en la solución de los filósofos cena

Comencemos con la clase más sencilla: Chopstick. El propósito de la Chopstick es simplemente existe como un chopstick y ser capaz de identificar propio. Como resultado, la implementación es una clase simple que tiene una variable de miembro de identificador, un constructor que inicializa el identificador y un método para devolver el identificador.

class Chopstick{
  const std::string m_Id;
public:
  Chopstick(std::string && Id):m_Id(Id){};
  const std::string GetID() {
    return m_Id;
  };
};

Es importante tener en cuenta que la m_Id de campo de identificador es una variable miembro constante. No quiero chopstick que cualquier estado de su propio obtener puede cambiar accidentalmente por otro subproceso. Además, observe que el constructor utiliza una referencia r-value en su lista de parámetros (el &&). Esto es una construcción de idioma nuevo en 2010 de Visual Studio que forma parte del borrador de C ++ 0 x estándar. Una referencia r-value aquí permite que el compilador evitar una llamada de constructor copia y mueva ID m_Id dentro de la instancia Chopstick si identificador es una variable temporal o r-value.

Bloques de mensajes Y mensajes

ChopstickProvider también es sencillo. Se puede considerarse como un búfer de almacenamiento cuyo propósito es para Aceptar Chopsticks cuando se envían a él y proporcionar Chopsticks a una Filósofa si se solicita.

Esto es el primer lugar en la utilizar el agente API. Uso lo que se conoce como un bloque de mensaje para el ChopstickProvider. Un bloque de mensaje se define como una clase que pueda enviar y recibir mensajes. Más específicamente, si estás pueda enviar un bloque de mensaje a mensaje, el bloque se denomina un destino y, si estás puede recibir un mensaje de un bloque de mensaje, se conoce como un origen de.

En este ejemplo, necesitaré el ChopstickProvider ser un origen y un destino porque los filósofos se se tanto recibir chopsticks desde el ChopstickProviders cuando los filósofos se hambrientos y envío a volver a la ChopstickProviders cuando los filósofos esté listos para volver a considerar. Este es un ejemplo de colocar que funcionen:

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

Puede ver que, después de declarar un chopstick y un chopstickBuffer (una instancia de la ChopstickProvider aún sin definir), Concurrency::send se utiliza para colocar la dirección de chopstick en el ChopstickBuffer y Concurrency::receive devuelve un puntero a un Chopstick desde el chopstickBuffer. Concurrency::Send es un método de plantilla que coloca sincrónicamente un mensaje en un bloque de mensaje. Concurrency::Receive es un método de plantilla que devuelve un mensaje de un bloque de mensaje. Concurrency::Receive se bloquea hasta que un mensaje esté disponible en el bloque de mensaje.

La biblioteca de agentes asincrónica también proporciona Concurrency::asend y Concurrency::try_receive. Concurrency::asend asincrónicamente genera una tarea para enviar el mensaje sin tener que esperar confirmación de recibo. Concurrency::try_receive es una llamada que no sea de bloqueo que se adquiera un mensaje si hay alguno disponible.

Ahora vamos a definir el ChopstickProvider realmente. ¿Recuerde dice que es sencillo? Es typedef:

typedef Concurrency::unbounded_buffer<Chopstick*> 
  ChopstickProvider;

La biblioteca de agentes proporciona un bloque de mensaje denominado el unbounded_buffer que tiene la funcionalidad que necesita. Un unbounded_buffer es una clase plantilla que es un origen y destino. Almacena un número limitado de memoria de mensajes en una cola interna. Los mensajes se quitan de la cola cuando las solicita. No tendrá la calidad independiente en esta implementación de Dining filósofos como hay un número limitado de chopsticks que se va moviendo entre cada titular chopstick. La funcionalidad clave que le interesa desde el unbounded_buffer es su semántica de movimiento.

Los agentes Y el bloque de mensajes combinación

Ahora echemos un vistazo a la implementación de la clase Filósofa. Esto es la parte interesante del problema Dining filósofos, donde asegurarse de que cada filósofa se está ejecutando en su propio subproceso y de que acceso al recurso compartido está coordinado correctamente. La clase Filósofa utiliza dos construcciones adicionales de la biblioteca de agentes asincrónica, la clase abstracta agente y la construcción de combinación. Primero voy a describir el agente y cómo se utiliza.

La clase de agente es una clase abstracta que modela lo que a veces se denomina una trama de actor o la programación en función de actor. Los agentes están pensados para utilizarse para facilitar las dependencias de eliminación y estado entre los componentes activos de una aplicación haciendo que los agentes de encapsular el estado y comunicarse entre sí únicamente a través de mensajes que pasan a través de un pequeño conjunto de interfaces públicas compartido. Es este sonidos más complicado que, pero espero que puede ver las similitudes con la clase Filósofa.

Como mencioné anteriormente, la clase Filósofa necesita estar ejecutándose en su propio subproceso. Esta traducción de la terminología de agentes, que esto significa que debe ser una tarea activa, así que Filósofa se va a derivar Concurrency::agent públicamente. La clase Filósofa también es necesario tener en cuenta dos ChopstickProviders (uno para cada mano) y, a continuación, utilizarlas para transición correctamente entre el pensamiento y comer. No la clase Filósofa está completa aún, pero se puede ver cómo esto está iniciando traducir en una sinopsis de clase:

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

Filósofa tiene un puntero a dos ChopstickProviders, un nombre, el número de bites o activa el Filósofa tardará, y un constructor para la inicialización.

Sin embargo, lo que falta, es una forma para inicializar la ChopstickProviders en el agente. No desea inicializarlos en tiempo de construcción porque los filósofos son independientes de las Chopsticks y sus ChopstickProviders. Se podría crear un método público adicional como AssignChopsticks (ChopstickProvider * izquierda, derecha ChopstickProvider *), pero, a continuación, tendría que tenga algunos cuidado, o un bloqueo, para garantizar que este método es seguro para la ejecución de subprocesos.

En su lugar, voy a crear una interfaz pública para que se pueden asignar el ChopstickProviders asincrónicamente. Hacerlo si agrega dos variables de miembro de unbounded_buffer más públicas y Utilícelas para enviar los ChopstickProviders dos filósofos:

Concurrency::unbounded_buffer<ChopstickProvider*> 
  LeftChopstickProviderBuffer;
Concurrency::unbounded_buffer<ChopstickProvider*> 
  RightChopstickProviderBuffer;

Ahora estoy listo para iniciar la implementación del método Filósofa que se ejecutan en su propio subproceso, espere a que se va a inicializar el ChopstickProviders y a continuación, inicie alternando pensando en y comer. Implementa el virtual método de ejecución de la clase base del agente. se iniciará Agent::Run en su propia tarea cuando se realiza una llamada a agent::start.

void run() {

Lo primero que necesita en ejecutar es inicializar el ChopstickProviders por en espera de los mensajes se envíen en los métodos públicos con recepción:

//initialize the ChopstickProviders
m_LeftChopstickProvider  = 
  Concurrency::receive(LeftChopstickProviderBuffer);
m_RightChopstickProvider = 
  Concurrency::receive(RightChopstickProviderBuffer);

Ahora necesito transición entre el pensamiento y comer. Para ello voy a utilizar dos métodos adicionales, PickupChopsticks y PutDownChopsticks:

for(int i = 0; i < m_Bites;++i) {
  Think();
  std::vector<Chopstick*> chopsticks(PickupChopsticks());
  Eat();
  PutDownChopsticks(chopsticks);
}

Lo único que queda para implementar en el método de ejecución es para limpiar devolviendo el ChopstickProviders para que se puede ser volver a usar si es necesario y establecer el estado del agente en realizado.

Concurrency::send(LeftChopstickProviderBuffer,  
  m_LeftChopstickProvider);
Concurrency::send(RightChopstickProviderBuffer, 
  m_RightChopstickProvider);

this->done(Concurrency::agent_done);
}

Mover en, los métodos adicionales Eat y reflexión son sencillos. En el problema original se describen como bucles número aleatorio:

private:
  void Eat() {
    RandomSpin();
  };

  void Think() {
    RandomSpin();
  };
};

Implementación PickupChopsticks es considerablemente más interesante porque encarga de adquirir y liberar las chopsticks sin introducir interbloqueos o las condiciones de carrera. Para implementar esta voy a utilizar otro bloque de mensajería de la biblioteca agentes asincrónica denomina combinación. Concurrency::JOIN es un bloque de mensaje que espera para mensajes procedentes de varios orígenes, posiblemente de distintos tipos. Una vez han recibido todos los mensajes, genera un mensaje acumular. Una combinación puede admitir fuentes de la misma o en varios tipos y adquirir los mensajes de una manera avaricioso o no avaricioso. Esto significa que hay cuatro variantes de combinación en la biblioteca de agentes. La clase Filósofa usa una combinación no avaricioso de tipo único. la figura 3 muestra un ejemplo sencillo de una combinación en el código.

Figura 3 A combinación simple

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

Ahora estoy listo para implementar PickupChopsticks. Lo primero que tenga en cuenta es que devolver un conjunto de chopsticks devolviendo un vector. Para obtener un par de chopsticks desde los medios de proveedor chopstick mediante una combinación no avaricioso. La variante no avaricioso de combinación espera a que para una oferta de mensajes de todos los orígenes vinculados y, a continuación, una vez que tiene una oferta de cada origen, confirma que realmente pueden tomar posesión del mensaje. Esto es lo que evita interbloqueo en este ejemplo. Si había utilizado avaricioso como el parámetro de plantilla para la combinación, el mensaje podría realizarse tan pronto como se realiza una oferta y o temprano cada Filósofa tendrían un solo chopstick y podrían todos se interbloqueado.

Éste es el código para PickupChopsticks. Crear una combinación, vincularlo a los proveedores de chopstick y esperar los chopsticks llegue a:

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

Implementación PutDownChopsticks es sencillo así. Todo lo necesita hacer es devolver los chopsticks que han adquirido desde el vector mediante la asend asincrónica del método:

void PutDownChopsticks(std::vector<Chopstick*>& v) {
  Concurrency::asend(m_LeftChopstickProvider,v[0]);
  Concurrency::asend(m_RightChopstickProvider,v[1]);
};

Probar la Filósofa Y mostrar el estado

La clase Filósofa puede utilizarse como es, pero debe saber cómo iniciar la clase Filósofa y tiene una manera de realizar informes sobre su estado, si está comer o pensando en.

Para iniciar el filósofa, llame al método agent::start, que genera una tarea que invoca el método de ejecución virtual. Para informar sobre el estado, presentar dos bloques de mensajes más de la biblioteca de agentes: Concurrency::overwrite_buffer y Concurrency::call.

Concurrency::overwite_buffer <t> es un bloque de mensaje que se almacena un valor único que puede sobrescribirse y genera una copia del valor cuando se solicite. Como con los bloques de mensajes, overwrite_buffer puede vincularse a destinos adicionales y propagación de mensajes ocurre en orden. Va agregar una variable miembro público la clase Filósofa que es un overwrite_buffer <philosopherstate> y actualizarla enviando mensajes a ella como las transiciones de Filósofa piensen Eating. En concreto, esto no implicará agregar una variable miembro a la clase Filósofa:

Concurrency::overwrite_buffer<PhilosopherState> CurrentState;

También significa modificar Eat y reflexión para actualizar el estado:

void Eat() {
  send(&CurrentState,PhilosopherState::Eating);
  RandomSpin();
};

void Think() {
  send(&CurrentState,PhilosopherState::Thinking);
  RandomSpin();
};

Concurrency::Call <t> es un bloque de mensaje que se crea con un functor y genera una tarea para ejecutar el functor cuando recibe el mensaje. Puede utilizar la llamada en combinación con la overwrite_buffer recién definido en el Filósofa para informar sobre su estado, tal como se muestra en la figura 4 .

Estado de la figura 4 informe Filósofa

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

Y eso es todo para la clase Filósofa.

Implementación de la clase de tabla

Ya ha visto que todo lo necesario para crear la clase de la tabla. Como se mencionó anteriormente, contiene un conjunto de los filósofos, un conjunto de Chopsticks y un conjunto de ChopstickProviders. La tabla será responsable de seating los filósofos en la tabla, colocar un ChopstickProvider entre cada uno de ellos y, colocando un Chopstick en cada uno de los ChopstickProviders. Para la máxima flexibilidad he elegido para que sea una clase de plantilla, y contiene una referencia a una lista de los filósofos, un vector de Chopsticks y un vector de los poseedores de Chopstick.

template<class PhilosopherList>
class Table {
  PhilosopherList & m_Philosophers;
  std::vector<ChopstickProvider*> m_ChopstickProviders;
  std::vector<Chopstick*> m_Chopsticks;
  ...

El método sólo público de la clase de la tabla es el constructor, que inicializa los vectores y asigna ChopstickProviders cada Filósofa (consulte la La figura 5 ).

La figura 5 tabla constructor

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

Hora de comer

Por último, para implementar principal, necesite declarar la lista de filósofos, crear una tabla, iniciar los filósofos, enlazar un mecanismo de informes y esperar a comer (consulte la Figura 6 ).

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

Afortunadamente, está claro cómo la clase de agente y el mensaje asincrónico pasando que la biblioteca de agentes asincrónica proporciona pueden ayudar a evitar las dificultades de coordinación de acceso al estado compartido. Ha administra implementar el problema Dining filósofos sin utilizar un bloqueo explícito único y sin tener que llamar directamente a las API de subprocesos. También ha administrado mantener la implementación de los términos del dominio, en concreto, ha empleado tiempo trabajar con Chopsticks, los filósofos y tablas en contraposición a las matrices de enteros y semáforos.

Lo que hecho todavía, se agrega cualquier escalabilidad inherente a los filósofos. Esta aplicación, tal como está escrita no ejecuta considerablemente más rápido en los más de cuatro núcleos. Pero esto se soluciona reemplazando la RandomSpin con un nombre más significativo, como un algoritmo implementado con paralelismo de basados en tareas con la biblioteca de trama paralelo (PPL). LO animo a leer sobre esto en elblog de simultaneidad nativo, donde tenemos el código fuente para este artículo, pero que también he agregado unos algoritmos de pensar en paralelo con personalización avanzada se implementa para demostrar composability entre el PPL y la biblioteca de agentes. He utilizado también características de administración en tiempo de la simultaneidad de ejecución recursos para administrar la calidad de los problemas de servicio que pueden surgir con una abundancia de trabajo paralelos. Cuando los recursos de procesamiento obtener escasos, puede asegúrese de que los filósofos todavía están capaz de obtener suficiente para comer.

Envíe sus preguntas y comentarios ammsync@Microsoft.com.

Rick Molloy es un administrador de programas en el equipo de plataforma informática paralelo de Microsoft.