Red de conocimiento informático - Aprendizaje de programación - ¿Cuándo se deben utilizar colas simultáneas?

¿Cuándo se deben utilizar colas simultáneas?

La cola concurrente es una cola ilimitada segura para subprocesos basada en nodos vinculados. Utiliza la regla de primero en entrar, primero en salir para ordenar los nodos, de modo que cuando agreguemos un elemento, se agregará a. al final de la cola, y cuando obtengamos un elemento, devolverá el elemento al principio de la cola. Se implementa mediante el algoritmo "sin espera", que es una modificación del algoritmo de Michael

& Scott.

Cola de entrada

La cola de entrada es donde se agrega el nodo de entrada al final de la cola. Para comprender fácilmente los cambios en la cola al ingresar a la cola y los cambios en el nodo principal y el nodo final, hice un diagrama de instantáneas de la cola para cada nodo agregado.

El primer paso es añadir el elemento 1. La cola actualiza el siguiente nodo desde el nodo principal al nodo del elemento 1. Al mismo tiempo, dado que los nodos de cola son iguales al nodo principal de forma predeterminada, su siguiente nodo apunta al nodo del elemento 1.

Paso 2 Añade el elemento 2. La cola primero establece el siguiente nodo del nodo del elemento 1 en el nodo del elemento 2 y luego actualiza el nodo de cola para que apunte al nodo del elemento 2.

El tercer paso es agregar el elemento 3 y establecer el siguiente nodo del nodo de cola en el nodo del elemento 3.

El cuarto paso es agregar el elemento 4, establecer el siguiente nodo del elemento 3 en el nodo del elemento 4 y luego apuntar el nodo de cola al nodo del elemento 4.

Al depurar el proceso de entrada a la cola y observar los cambios del nodo principal y del nodo final, podemos encontrar que la entrada a la cola hace principalmente dos cosas.

Lo primero es establecer el nodo de entrada en el nodo al lado del nodo de cola de la cola actual.

Lo segundo es actualizar el nodo de cola. Si el siguiente nodo del nodo de cola no está vacío, establezca el nodo de entrada de la cola en el nodo de cola. establezca el nodo de entrada de la cola en el nodo de cola. El nodo de entrada se establece en el siguiente nodo en la cola, por lo que el nodo de cola no siempre es el nodo de cola. Comprender esto nos será muy útil para estudiar el código fuente.

A través del análisis anterior, podemos comprender el proceso de puesta en cola desde la perspectiva de la cola de un solo subproceso, pero la situación de múltiples subprocesos en cola al mismo tiempo se vuelve complicada, porque puede haber otros subprocesos en la cola. saltando. Si un subproceso está en cola, primero debe obtener el nodo de cola y luego configurar el nodo al lado del nodo de cola como nodo de cola, pero puede haber otro subproceso en cola en este momento, por lo que el nodo de cola de la cola cambiará. El hilo actual suspenderá la operación de puesta en cola y luego volverá a adquirir el nodo de cola. Repasemos el código fuente y analicemos en detalle cómo utiliza el algoritmo CAS para ingresar a la cola. public?boolean?offer(E?e)? Node(e);

retry:

// Bucle infinito, la entrada repetida a la cola es fracasado.

for(;;)?{

//Crea una referencia al nodo de cola

Nodo?t?=? tail; //p se utiliza para representar el nodo de cola de la cola, que de forma predeterminada es el nodo de cola.

Node?p?=?t; for?(int?hops?=?0;? ;hops++)?{ //Obtiene el siguiente nodo del nodo

Nodo. ?next?=?succ(p); //el siguiente nodo no es nulo, eso significa que p no es el nodo de cola, necesitas actualizar p antes de apuntar al siguiente nodo

if ?(next!!!)?{ //se repite dos veces o más y el nodo actual todavía no es igual al nodo de cola

if ?(hops?>?HOPS?&&?t ?! =?tail)

continue?retry;

p?=?next;

} //Si p es el nodo de cola, establece el siguiente El nodo del nodo p será el nodo entrante. ¿De lo contrario?{

// Si el nodo de cola tiene 1 nodo siguiente mayor o igual, el nodo entrante se establece en el nodo TAIR. No importa si la actualización falla porque la falla significa que otros subprocesos actualizaron exitosamente el nodo TAIR.

if?(hops?>=?HOPS)

casTail(t,?n);?//?Actualiza el nodo de cola, permitiendo fallas

return ?true; }

/?p tiene el siguiente nodo, lo que indica que el siguiente nodo de p es el nodo de cola y luego restablece el nodo p

else? >

}

Desde la perspectiva del código fuente, todo el proceso de unirse al equipo hace principalmente dos cosas.

El primer paso es localizar el nodo de cola. El nodo de cola no siempre es el nodo de cola, por lo que cada entrada de la cola debe encontrar primero el nodo de cola a través del nodo de cola, que puede ser el nodo de cola o el nodo al lado del nodo de cola. El primer if en el cuerpo del bucle de código es determinar si hay un siguiente nodo en la cola. Si lo hay, el siguiente nodo puede ser el nodo de cola. Al obtener el siguiente nodo del nodo de cola, debe prestar atención a si el nodo p es igual al nodo siguiente p. La única posibilidad es que tanto el nodo p como el nodo siguiente p sean iguales a vacíos, lo que significa que. la cola acaba de inicializarse y se está preparando para agregar el primer nodo, por lo que necesita devolver el nodo principal. El código para obtener el siguiente nodo del nodo p es el siguiente final?Node?succ(Node?p)?{ Node?next? =?p. getNext();

return?(p?==?next)head?:?next }

El segundo paso es configurar el nodo entrante como nodo de cola. Si p está vacío, significa que p es el nodo de cola de la cola actual; si no está vacío, significa que otros subprocesos han actualizado el nodo de cola y luego es necesario volver a adquirir el nodo de cola de la cola actual.

Intención de diseño de salto. El análisis anterior muestra lo que se debe hacer para la cola de primero en entrar, primero en salir, es decir, establecer el nodo de entrada como el nodo de cola. El código y la lógica escritos por Doug Lea son aún un poco más complicados.

Entonces, ¿qué pasa si implemento la cola de la siguiente manera? public?boolean?offer(E?e)?{

if?(e?==?null)

throw?new?NullPointerException(); e>?n?=?nuevo?Nodo( e); para?(;;)?{ Nodo?t?=?cola

if?(t.casNext(null,?n)?{ return?true; } } }

}

Para que el nodo de cola sea siempre el nodo de cola de la cola, Solo se puede implementar con una pequeña cantidad de código y la lógica es muy clara y fácil de entender. Sin embargo, la desventaja de esto es que necesita usar CAS cíclico para actualizar el nodo de cola cada vez. CAS se puede usar para actualizar el nodo de cola y se puede mejorar la eficiencia de la entrada de la cola. douglea usa la variable hops para controlar y reducir la frecuencia de las actualizaciones del nodo de cola. no se actualiza cada vez que el nodo ingresa a la cola, pero cuando

el nodo de cola es mayor o igual al valor de la constante HOPS, cuanto mayor sea la distancia entre el nodo de cola y el nodo de cola, menos veces se usa CAS para actualizar el nodo de cola, pero cuanto mayor es la distancia, más tiempo lleva localizar el nodo de cola cada vez que el nodo ingresa a la cola. Cuanto mayor es el tiempo, porque el cuerpo del bucle necesita hacerlo. realice un bucle más para ubicar el nodo de cola, pero esto aún mejora la eficiencia de la entrada en la cola, porque en esencia, reduce la cantidad de lecturas de variables volátiles al aumentar la cantidad de lecturas de variables volátiles y la cantidad de escrituras en volátiles. variables es mucho mayor que la lectura, por lo que la cola es más eficiente ?private?static?final?int?HOPS?=?1;

Otro punto a tener en cuenta es que el método de la cola siempre devuelve verdadero, por lo que no utilice el valor de retorno para determinar si la cola tiene éxito.

4. Dequeue

Devuelve el elemento del nodo de la cola y borra la referencia del nodo al elemento. tome una instantánea de cada nodo que sale de la cola y vea qué sucede con el nodo principal.

Desde arriba, podemos ver en la figura que el nodo principal no se actualiza cada vez que sale de la cola. Cuando hay un elemento en el nodo principal, el elemento en el nodo principal aparecerá directamente y el nodo principal no se actualizará. La operación de eliminación de cola solo actualizará el nodo principal cuando no haya elementos en el nodo principal. Este método también reduce el consumo de CAS para actualizar el nodo principal a través de la variable de recuento de saltos, mejorando así la eficiencia de la salida de la cola. Repasemos el código fuente. ()?{

Nodo?h?=?head;?p representa el nodo principal, que debe eliminarse de la cola. Nodo Nodo?p ?=?h; for?(int?hops?=?0;;?hops++) ?{ //?Obtener el elemento del nodo p E?item?=?p.getItem (); El nodo p no es nulo, use CAS para establecer el elemento al que hace referencia el nodo p en nulo. Si tiene éxito, devuelva el elemento del nodo p. if(item! =?null?&&?p.casItem(item,?null))?{ if(hops?>=?HOPS)?{ //Establece el nodo p como el siguiente nodo Nodo del nodo principal?q?=?p.getNext(); updateHead(h,? (q! =?null)q?:?p); ?Si los elementos del nodo principal están vacíos o el nodo principal ha cambiado, significa que el nodo principal ha sido modificado por otros subprocesos.

Luego obtenga el siguiente nodo de p node

Node?next?=?succ(p);

/?Si el siguiente nodo de p también es null , entonces esta cola está vacía

if(next?==?null)?{

//?Actualiza el nodo principal. updateHead(h,?p); break; } //Si el siguiente elemento no está vacío, establezca el siguiente nodo del nodo principal en el nodo principal

p?=?next; =?next; } return? }

Primero obtenga el elemento del nodo principal y luego determine si el elemento del nodo principal está vacío, significa que ya ha accedido otro hilo. el nodo. El elemento ha realizado una operación fuera de la cola. Si no está vacío, use CAS para establecer la referencia del nodo principal en nulo. Si CAS tiene éxito, el elemento del nodo principal se devuelve directamente. CAS no tiene éxito, significa que se ha ejecutado otro subproceso. El uso de una operación fuera de cola para actualizar el nodo principal hace que el elemento cambie, por lo que es necesario volver a adquirir el nodo principal.