Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Dos formas de programación en Orleans son relevantes para los granos:
- Programación de solicitudes: programar llamadas de grano entrantes para su ejecución según las reglas descritas en Programación de solicitudes.
- Programación de tareas: programación de bloques sincrónicos de código para que se ejecuten de forma uniproceso .
Todo el código de grano se ejecuta en el programador de tareas del grano, lo que significa que las solicitudes también se ejecutan en el programador de tareas del grano. Incluso si las reglas de programación de solicitudes permiten que varias solicitudes se ejecuten simultáneamente, no se ejecutarán en paralelo porque el programador de tareas del grano siempre ejecuta tareas una por una y nunca ejecuta varias tareas en paralelo.
Programación de tareas
Para comprender mejor la planificación, considere lo siguiente: MyGrain
. Tiene un método denominado DelayExecution()
que registra un mensaje, espera algún tiempo y, a continuación, registra otro mensaje antes de devolverlo.
public interface IMyGrain : IGrain
{
Task DelayExecution();
}
public class MyGrain : Grain, IMyGrain
{
private readonly ILogger<MyGrain> _logger;
public MyGrain(ILogger<MyGrain> logger) => _logger = logger;
public async Task DelayExecution()
{
_logger.LogInformation("Executing first task");
await Task.Delay(1_000);
_logger.LogInformation("Executing second task");
}
}
Cuando se ejecuta este método, el cuerpo del método se ejecuta en dos partes:
- La primera
_logger.LogInformation(...)
llamada y la llamada aTask.Delay(1_000)
. -
_logger.LogInformation(...)
Segunda llamada.
La segunda tarea no está programada en el programador de tareas de granos hasta que se complete la llamada Task.Delay(1_000)
. Programa en ese momento la continuación del método granular.
Esta es una representación gráfica de cómo se programa y ejecuta una solicitud como dos tareas:
La descripción anterior no es específica de Orleans; describe cómo funciona la programación de tareas en .NET. El compilador de C# convierte métodos asincrónicos en una máquina de estado asincrónica y la ejecución avanza a través de esta máquina de estado en pasos discretos. Cada paso se programa en el actual TaskScheduler (al que se accede a través de TaskScheduler.Current, siendo el valor predeterminado TaskScheduler.Default) o en el actual SynchronizationContext. Si se usa un TaskScheduler
, cada paso del método representa una Task
instancia que se pasa a ese TaskScheduler
. Por lo tanto, un Task
en .NET puede representar dos cosas conceptuales:
- Una operación asincrónica que se puede esperar. La ejecución del método
DelayExecution()
mencionado se representa mediante unTask
que se puede awaitar. - Bloque de trabajo sincrónico. Cada fase del
DelayExecution()
método anterior se representa mediante .Task
Cuando TaskScheduler.Default
se utiliza, las continuaciones se programan directamente sobre .NET ThreadPool y no se encapsulan en un objeto Task
. El ajuste de las continuaciones en instancias de Task
se realiza de forma transparente, por lo que los desarrolladores rara vez necesitan preocuparse por estos detalles de implementación.
Programación de tareas en Orleans
Cada activación de cada grano tiene su propia TaskScheduler
instancia que se encarga de aplicar el modelo de ejecución de un solo subproceso. Internamente, esto TaskScheduler
se implementa a través de ActivationTaskScheduler
y WorkItemGroup
.
WorkItemGroup
mantiene las tareas en cola en Queue<T> (donde T
es internamente un Task
) e implementa el IThreadPoolWorkItem. Para ejecutar cada elemento actualmente en cola Task
, WorkItemGroup
se programa a sí mismo en .NET ThreadPool
. Cuando .NET ThreadPool
invoca el método WorkItemGroup
de IThreadPoolWorkItem.Execute()
, WorkItemGroup
ejecuta las instancias Task
encoladas una por una.
Cada grano tiene un programador que se ejecuta mediante su propia programación en .NET ThreadPool
.
Cada programador contiene una cola de tareas:
.NET ThreadPool
ejecuta cada elemento de trabajo que se encola en él. Esto incluye programadores de granos así como otros elementos de trabajo, como los programados a través Task.Run(...)
.
Nota:
El programador de un grano solo se puede ejecutar en un subproceso a la vez, pero no siempre se ejecuta en el mismo subproceso. .NET ThreadPool
es libre de usar un subproceso diferente cada vez que se ejecuta el programador del grano. El programador de grain garantiza que solo se ejecuta en un hilo a la vez, implementando el modelo de ejecución de un solo hilo de grains.