Usar variables locales de tareas
Las variables locales de tareas son coherentes con el ciclo. Solo las escribe una tarea definida en un ciclo de tareas, mientras que todas las demás tareas solo tienen acceso de lectura. Se tiene en cuenta que las tareas pueden ser interrumpidas por otras tareas o pueden ejecutarse al mismo tiempo. La coherencia de ciclo también se aplica sobre todo si la aplicación se ejecuta en un sistema con un procesador multinúcleo.
El uso de listas de variables globales locales de tareas es, por lo tanto, una forma de obtener sincronización automáticamente (por parte del compilador) cuando varias tareas editan las mismas variables. Este no es el caso cuando se utilizan GVL normales. Varias tareas pueden escribir simultáneamente en variables GVL normales durante un ciclo.
Tenga en cuenta, sin embargo, que la sincronización de las variables locales de la tarea requiere relativamente mucho tiempo y memoria y no es el medio adecuado en todas las aplicaciones. Consulte a continuación para obtener información técnica más detallada y consejos de mejores prácticas para ayudarlo a decidir.
En el CODESYS proyecto, el Lista de variables globales (tasklocal) El objeto está disponible para definir variables locales de tareas. Sintácticamente corresponde a un GVL normal, pero también contiene la información de la tarea que tiene acceso de escritura a las variables. Entonces, ninguna otra tarea cambia todas las variables en dicha GVL durante el ciclo de una tarea.
Sugerencia
Las variables tasklocal son variables complejas cuyo valor no se puede cambiar en modo en línea utilizando el Escribir valores dominio.
En la siguiente sección encontrará un ejemplo simple que muestra el principio y la funcionalidad de las variables locales de tareas: hay un programa de escritura y uno de lectura. Los programas se ejecutan en diferentes tareas, pero acceden a los mismos datos que se encuentran en una lista de variables globales locales de tareas para que se procesen de manera consistente en el ciclo.
Funcionalidad mostrada en un ejemplo
Las instrucciones para reprogramar esta aplicación de muestra se pueden encontrar a continuación.
(* task-local GVL, object name: "Tasklocals" *) VAR_GLOBAL g_diaData : ARRAY [0..99] OF DINT; END_VAR PROGRAM ReadData VAR diIndex : DINT; bTest : BOOL; diValue : DINT; END_VAR bTest := TRUE; diValue := TaskLocals.g_diaData[0]; FOR diIndex := 0 TO 99 DO bTest := bTest AND (diValue = Tasklocals.g_diaData[diIndex]); END_FOR PROGRAM WriteData VAR diIndex : DINT; diCounter : DINT; END_VAR diCounter := diCounter + 1; FOR diIndex := 0 TO 99 DO Tasklocals.g_diaData[diIndex] := diCounter; END_FOR
Los programas Escribir datos y Leer datos son llamados por diferentes tareas.
en programa WriteData
se convierte en la matriz g_diaData
llena de valores. El programa ReadData
prueba si los valores de la matriz son los esperados. Si este es el caso, la variable devuelve bTest
como resultado TRUE
.
Los datos de matriz que se están probando son sobre la variable g_diaData
en el objeto Tasklocals
del tipo Globale Variablenliste (tasklokal)
declarado. Esto sincroniza el acceso a los datos en el compilador y se garantiza que los datos serán coherentes con el ciclo, incluso si los programas de acceso se llaman desde diferentes tareas. En el programa de muestra esto significa específicamente que la variable test
en programa ReadData
siempre TRUE
es.
Si la variable g_diaData
solo se declaró como una lista de variables globales en este ejemplo, la prueba, es decir, la variable test
en programa ReadData
, más a menudo FALSE
entregar. Porque en este caso una de las dos tareas en el FOR
-El bucle puede ser interrumpido por la otra tarea, o ambas tareas pueden ejecutarse al mismo tiempo (controladores multinúcleo). Y así, el escritor podría cambiar los valores mientras el lector lee la lista.
Restricciones de declaración
Importante
Después de realizar cambios en las declaraciones en la lista de variables locales de la tarea, no es posible un cambio en línea de la aplicación.
Tenga en cuenta lo siguiente cuando declare una lista de variables locales de tareas globales:
No asigne direcciones directas a través de una declaración AT.
No asigne variables de tareas locales en la configuración del PLC.
No declarar punteros.
No declarar referencias.
No cree instancias de bloques de funciones.
No declarar variables locales de tareas al mismo tiempo
PERSISTENT
yRETAIN
.
El compilador informa como un error un acceso de escritura en una tarea sin permiso de escritura. Sin embargo, no es posible determinar todos los lugares donde se violan los derechos de escritura. El compilador solo puede asignar llamadas estáticas a una tarea. Sin embargo, la llamada de un bloque de funciones a través de un puntero o una interfaz no se asigna a una tarea, por ejemplo. Esto significa que los accesos de escritura tampoco se registran allí. Además, los punteros pueden apuntar a variables locales de tareas. De esta manera, los datos pueden ser manipulados en una tarea de lectura. En este caso, tampoco se emite ningún error de tiempo de ejecución. Sin embargo, los valores cambiados al acceder a través de punteros no se vuelven a copiar en la referencia común de las variables.
Propiedades de las variables globales locales de tareas y posible comportamiento
Las variables están en la lista para cada tarea en una dirección diferente. Eso significa para el acceso de lectura: ADR(variable name)
devuelve una dirección diferente en cada tarea.
El mecanismo de sincronización garantiza lo siguiente:
consistencia del ciclo
Libertad de estados de bloqueo: en ningún momento una tarea espera una acción de otra tarea.
Sin embargo, este método no se puede utilizar para determinar un punto en el tiempo en el que una tarea de lectura definitivamente recibirá una copia de la tarea de escritura. En principio, las copias pueden divergir. En el ejemplo anterior, no se puede suponer que cada copia escrita será editada una vez por el lector. Por ejemplo, la tarea de lectura puede procesar la misma matriz durante varios ciclos, o el contenido de la matriz puede "saltar" uno o más valores entre dos ciclos. Ambas pueden ocurrir y deben ser tenidas en cuenta.
La tarea de escritura se puede detener durante un ciclo entre dos accesos a la referencia común por cada tarea de lectura. Eso significa que si n
existen tareas de lectura, la tarea de escritura n
Los ciclos se retrasan hasta la próxima actualización de la referencia compartida.
La tarea de escritura puede evitar que una tarea de lectura obtenga una copia leída en cada ciclo. Por lo tanto, no es posible especificar un número máximo de ciclos después de los cuales una tarea de lectura definitivamente recibirá una copia.
En particular, esto puede volverse problemático cuando se trata de tareas de ejecución muy lenta. Suponiendo que una tarea solo se ejecuta cada hora y luego no puede acceder a las variables locales de la tarea, entonces la tarea está trabajando con una copia muy antigua de la lista. Por lo tanto, puede tener sentido insertar una marca de tiempo en las variables locales de la tarea, que las tareas de lectura pueden usar para al menos determinar si la lista está actualizada. Puede agregar una marca de tiempo de la siguiente manera: Agregar una variable de tipo a la lista de variables locales de tareas LTIME
y en la tarea de escritura, por ejemplo, el siguiente código: tasklocal.g_timestamp := LTIME();
.
mejores prácticas
Las variables locales de tareas están diseñadas para el caso de uso "escritor único - lectores múltiples". Cuando se implementa código que será llamado por diferentes tareas, el uso de variables locales de tareas es de gran beneficio. Por ejemplo, este es el caso con la aplicación de muestra descrita anteriormente appTasklocal
el caso cuando se extiende por varias tareas de lectura que acceden a la misma matriz y usan las mismas funciones.
Las variables locales de tareas son especialmente útiles en sistemas con procesadores multinúcleo. No puede sincronizar tareas por prioridad en estos sistemas. Entonces surge la necesidad de otros mecanismos de sincronización.
No utilice variables locales de tarea si una tarea de lectura siempre debe funcionar en la última copia de la variable. Las variables de tareas locales no son adecuadas para esto.
Un problema similar es el problema "productor - consumidor". Este es el caso cuando una tarea produce datos y una segunda tarea los procesa. Con esta constelación, prefiera un tipo diferente de sincronización. Por ejemplo, el productor podría usar una bandera para indicar que hay una nueva fecha disponible. El consumidor puede usar una segunda bandera para indicar que ha procesado sus datos y ahora está esperando una nueva entrada. Ambos pueden trabajar con los mismos datos. No hay sobrecarga por la copia cíclica de los datos y el consumidor no pierde ningún dato generado por el productor.
supervisión
En tiempo de ejecución, hay varias copias, posiblemente diferentes, de la lista de variables locales de la tarea en la memoria. Sin embargo, no todos los valores se pueden mostrar al monitorear una posición. Por esta razón, los valores de la referencia común se muestran para una variable local de tarea en el monitoreo en línea, en la lista de monitoreo y en la visualización.
Si establece un punto de interrupción, se muestran los datos de la tarea que alcanzó el punto de interrupción y, en consecuencia, se detuvo. Mientras tanto, las otras tareas continúan ejecutándose. Bajo ciertas circunstancias, la copia común se puede cambiar. Sin embargo, en el contexto de la tarea detenida, los valores permanecen sin cambios y se muestran como tales. Debes ser consciente de esto.
Antecedentes: implementación técnica
Para una lista de variables locales de tareas, el compilador crea una copia para cada tarea y una copia de referencia común para todas las tareas. Se crea una estructura que contiene las mismas variables que la lista de variables locales de tareas. También se crea una matriz con esta estructura, y se crea una dimensión de matriz para cada tarea. Por lo tanto, se indexa un elemento de matriz para cada tarea. Si ahora se accede a una variable de la lista en el código, en realidad se accede a la copia local de la tarea de la lista. Además, se determina en qué tarea se está ejecutando actualmente el bloque y el acceso se indexa en consecuencia.
Por ejemplo, la línea de código diValue := TaskLocals.g_diaData[0];
reemplazado del ejemplo anterior:
diValue := __TaskLocalVarsArray[__CURRENTTASK.TaskIndex].__g_diarr[0];
__CURRENTTASK
es un operador que ab CODESYS V3.5 SP13 está disponible para determinar rápidamente el índice de tareas actual.
En tiempo de ejecución, el contenido de la lista local de tareas se copia en la referencia común al final de la tarea de escritura. En el caso de una tarea de lectura, el contenido de la referencia común se copia en la copia local de la tarea al principio. Por lo tanto, hay n+1 copias de la lista para n tareas: una lista sirve como referencia común y, además, cada tarea tiene su propia copia de la lista.
Un programador controla la ejecución oportuna de múltiples tareas y, por lo tanto, el cambio de tareas. La estrategia seguida por el planificador para controlar la asignación del tiempo de ejecución tiene como objetivo evitar el bloqueo de una tarea. Por lo tanto, el mecanismo de sincronización está optimizado para las propiedades de las variables locales de la tarea, de modo que se evitan los estados de bloqueo (estados bloqueados) y una tarea nunca espera la acción de otra tarea.
estrategia de sincronización:
Siempre que la tarea de escritura escriba una copia en la referencia compartida, ninguna de las tareas de lectura obtendrá una copia.
Siempre que una tarea de lectura obtenga una copia de la referencia compartida, la tarea de escritura no vuelve a escribir una copia.
Instrucciones para crear la aplicación de ejemplo descrita anteriormente
Objetivo: quieres empezar con un programa ReadData
acceder a los mismos datos a los que accede un programa WriteData
para ser escrito. Los dos programas deben ejecutarse en tareas diferentes. Usted proporciona los datos en una lista de variables locales de la tarea para que se procesen automáticamente de forma coherente con el ciclo.
Requisito: Un proyecto estándar recién creado y abierto en el editor.
Cambiar el nombre de la aplicación de
Application
enappTasklocal
alrededor.Agrega abajo
appTasklocal
un programa en ST por nombreReadData
adicional.Agrega abajo
appTasklocal
otro programa en ST por nombreWriteData
adicional.Asigne un nombre a la tarea predeterminada
MainTask
bajo el objetoTaskkonfiguration
enRead
alrededor.Añadir en el diálogo configuración la tarea
Read
a través del botón Agregar llamada llamando al programaReadData
adicional.Pegar debajo del objeto configuración de tareas agregó otra tarea nombrada con
Write
y agregue la llamada del programa a esta tareaWrite
adicional.Ahora hay dos tareas en la configuración de tareas
Write
yRead
quien los programasWriteData
respectivamenteReadData
llamar.Seleccione la aplicación
appTasklocal
y agregar un objeto de tipo Lista de variables globales (tarea local) adicional.El diálogo Agregar lista de variables globales (tarea local). se abre
Ingresar como nombre
Tasklocals
una.Elija de la lista desplegable Tarea con acceso de escritura la tarea
Write
.La estructura de objetos para usar variables de tareas locales dentro de una aplicación está completa. Ahora puede codificar los objetos como se muestra arriba en la descripción del ejemplo.