Bueno, creo que puedo responder esta pregunta de manera concisa.
Cuando vamos a decir si un objeto simultáneo es correcto, siempre tratamos de encontrar la manera de extender el orden parcial a un orden total.
podemos reconocer si un objeto secuencial es correcto mucho más fácilmente.
Primero, pongamos el objeto concurrente a un lado. Lo discutiremos más tarde. Ahora consideremos un historial secuencial H_S, un historial secuencial es una secuencia de eventos (es decir, Invocaciones y Respuestas) de la cual cada Invocación es seguida instantáneamente por su Respuesta correspondiente. (De acuerdo, "instantáneamente" podría confundirse, considere la ejecución de un programa de un único subproceso, por supuesto, hay un intervalo entre cada invocación y su respuesta, pero los métodos se ejecutan uno por uno. Por lo tanto, "instantáneamente" significa que ningún otro invocación/respuesta puede pegarse en un par de Invoke_i ~ Response_i)
el H_S puede parece:
H_S : I1 R1 I2 R2 I3 R3 ... In Rn
(Ii means the i-th Invoke, and Ri means the i-th Response)
será muy fácil de razonar acerca de la exactitud de la historia H_S, beca usar allí no es concurrencia, lo que tenemos que hacer es verificar si la ejecución funciona tan bien como lo esperamos (cumplir con las condiciones de la especificación secuencial). En otras palabras, es un legel historial secuencial.
Ok, la realidad es que estamos trabajando con un programa concurrente. Por ejemplo, ejecutamos dos hilos A y B en nuestro programa. Cada vez que ejecutamos el programa, obtendremos un historial de H_C (History_Concurrent) de la expulsión. Necesitamos considerar una llamada a método como Ii ~ Ri como en H_S. Por supuesto, debe haber muchas superposiciones entre llamadas de método llamadas por el subproceso A y el subproceso B. Pero cada evento (es decir, Invocaciones y Respuestas) tiene su orden en tiempo real. Por lo que los invoca y respuesta de todos los métodos llamados por A y B se pueden asignar a un orden secuencial, el orden puede verse como:
H_C : IA1 IB1 RA1 RB1 IB2 IA2 RB2 RA2
El orden parece estar confundido, es justo el tipo de los acontecimientos de cada método llama:
thread A: IA1----------RA1 IA2-----------RA2
thread B: | IB1---|---RB1 IB2----|----RB2 |
| | | | | | | |
| | | | | | | |
real-time order: IA1 IB1 RA1 RB1 IB2 IA2 RB2 RA2
------------------------------------------------------>time
Y tenemos el H_C. Entonces, ¿cómo podríamos verificar si la ejecución de H_C es correcta? Podemos reordenar la H_C a un H_RO siguiendo la regla:
REGLA:Si un método llamado m1 m2 precede a otro, entonces el mosto m1 m2 precede en la secuencia reordenada. (Significa que si Ri está delante de Ij en el H_C, debe garantizar que Ri todavía está delante de Ij en la secuencia reordenada, i y j no tienen sus órdenes, también podemos usar a, b, c ...) Decimos que H_C es equivalente a H_RO (history_reorder) bajo dicha regla.
El H_RO tendrá 2 propiedades:
- Se respeta el orden del programa.
- Conserva el comportamiento en tiempo real.
Reordenar la H_C sin violar la regla anterior, podemos obtener algunas historias secuenciales (que son equivalentes a H_C), por ejemplo:
H_S1: IA1 RA1 IB1 RB1 IB2 RB2 IA2 RA2
H_S2: IB1 RB1 IA1 RA1 IB2 RB2 IA2 RA2
H_S3: IB1 RB1 IA1 RA1 IA2 RA2 IB2 RB2
H_S4: IA1 RA1 IB1 RB1 IA2 RA2 IB2 RB2
Sin embargo, no podemos obtener una H_S5:
H_S5: IA1 RA1 IA2 RA2 IB1 RB1 IB2 RB2
porque IB1 ~ RB1 precede por completo a IA2 ~ RA2 en el H_C, no se puede reordenar.
Ahora, con estas historias secuenciales, ¿cómo podríamos confirmar si nuestra ejecución historia H_C es correcta? (Resalto la historia H_C, significa que nosotros sólo se preocupan por la corrección de la historia H_C ahora en lugar de la corrección de el programa concurrente)
la respuesta es sencilla, si al menos una de las historias secuenciales es correcta (una historia legal secuencial cumple con las condiciones de la especificación secuencial), entonces la historia H_C es linealizable, nos llaman el H_S legal linealización del H_C. Y H_C es una ejecución correcta. En otras palabras, es una ejecución legal que esperábamos. Si tiene experiencia en programación simultánea, debe haber escrito dicho programa que a veces parece bastante bien, pero a veces puede ser totalmente erróneo.
Ahora hemos sabido lo que es un historia linealizable de la ejecución de un programa concurrente. Entonces, ¿qué pasa con el programa concurrente en sí?
La idea básica detrás de instrucción atómica es que cada historia concurrente es equivalente, en el sentido siguiente, a una cierta historia secuencial. [El arte de la programación de multiprocesador 3.6.1: instrucción atómica] ("siguiente sentido" es la regla de reordenación he hablado más arriba)
Ok, la referencia puede ser un poco confuso. Significa que, si cada historia concurrente tiene una linealización (historia secuencial legal equivalente a ella), el programa concurrente cumple las condiciones de linealidad.
Ahora, hemos entendido lo que es Linearizability. Si decimos que nuestro programa concurrente es linealizable, en otras palabras, tiene la propiedad de linealidad. Significa que cada vez que lo ejecutamos, la historia es linealizable (el historial es lo que esperamos).
Por lo tanto es obvio que instrucción atómica es un de seguridad (corrección) propiedad.
Sin embargo, el método de la reordenación de todas las historias concurrentes a la historia secuencial para juzgar si un programa es linealizable sólo es posible en prinple. En la práctica, nos enfrentamos a miles de llamadas a métodos llamadas por subprocesos de dos dígitos.No podemos reordenar todas las historias de ellos. Ni siquiera podemos enumerar todas las historias concurrentes de un programa de trival.
La manera habitual de mostrar que una implementación de objeto concurrente es linealizable es identificar para cada método de un punto donde el efecto método TAKS linealización. [El Arte de Programar multiprocesador 3.5.1: puntos de linealización]
vamos a discutir sobre la cuestión en las condiciones de "objeto concurrente". Es sustancialmente igual que arriba. Una implementación de un objeto simultáneo tiene algunos métodos para acceder a los datos del objeto simultáneo. Y los multihilos compartirán un objeto concurrente. Por lo tanto, cuando acceden al objeto al mismo tiempo llamando a los métodos del objeto, el implementador del objeto concurrente debe garantizar la corrección de las llamadas al método simultáneo.
Identificará para cada método un punto de linealización. Lo más importante es comprender el significado del punto de linealización. La afirmación de "donde el método hace efecto" es realmente difícil de entender. Tengo algunos ejemplos:
primer lugar, vamos a ver un caso incorrecto:
//int i = 0; i is a global shared variable.
int inc_counter() {
int j = i++;
return j;
}
Es bastante fácil de detectar el error. Podemos traducir i ++ en:
#Pseudo-asm-code
Load register, address of i
Add register, 1
Store register, address of i
De modo que dos hilos pueden ejecutar uno "i ++;" concurrentemente y el resultado de i parece ser aumentado solo una vez. pudimos conseguir una H_C tales:
thread A: IA1----------RA1(1) IA2------------RA2(3)
thread B: | IB1---|------RB1(1) IB2----|----RB2(2) |
| | | | | | | |
| | | | | | | |
real-time order: IA1 IB1 RA1(1) RB1(1) IB2 IA2 RB2(2) RA2(3)
---------------------------------------------------------->time
Lo que intenta reordenar el orden de tiempo real, no se debe encontrar una historia secuencial Legel que equivale a H_C.
Nos debe reescribir el programa:
//int i = 0; i is a global shared variable.
int inc_counter(){
//do some unrelated work, for example, play a popular song.
lock(&lock);
i++;
int j = i;
unlock(&lock);
//do some unrelated work, for example, fetch a web page and print it to the screen.
return j;
}
bien, ¿cuál es el punto de linealización inc_counter()? La respuesta es toda la sección crítica. Porque cuando muchos hilos llaman repetidamente a inc_counter(), la sección crítica se ejecutará atómicamente. Y puede garantizar la exactitud del método. La respuesta del método es el valor incrementado de i global. Considere la H_C como:
thread A: IA1----------RA1(2) IA2-----------RA2(4)
thread B: | IB1---|-------RB1(1) IB2--|----RB2(3) |
| | | | | | | |
| | | | | | | |
real-time order: IA1 IB1 RA1(2) RB1(1) IB2 IA2 RB2(3) RA2(4)
Obviamente, la historia secuencial equivalente es legal:
IB1 RB1(1) IA1 RA1(2) IB2 RB2(3) IA2 RA2(4) //a legal sequential history
Tenemos reordenar la IB1 ~ ~ RB1 y IA1 RA1, porque se solapaban en el orden tiempo real , se pueden reordenar ambiguamente. En el caso de H_C, podemos razonar que la sección crítica de IB1 ~ RB1 se ingresa primero.
El ejemplo es demasiado simple. Vamos a considerar otro:
//top is the tio
void push(T val) {
while (1) {
Node * new_element = allocte(val);
Node * next = top->next;
new_element->next = next;
if (CAS(&top->next, next, new_element)) { //Linearization point1
//CAS success!
//ok, we can do some other work, such as go shopping.
return;
}
//CAS fail! retry!
}
}
T pop() {
while (1) {
Node * next = top->next;
Node * nextnext = next->next;
if (CAS(&top->next, next, nextnext)) { //Linearization point2
//CAS succeed!
//ok, let me take a rest.
return next->value;
}
//CAS fail! retry!
}
}
Es un algoritmo de pila sin bloqueo lleno de errores! pero no cuides los detalles. Solo quiero mostrar el punto de linealización de push() y pop().Los he mostrado en los comentarios. Considere que muchos hilos llaman repetidamente a push() y pop(), se ordenarán en el paso CAS. Y otros pasos parecen no tener importancia porque cualquiera que sea que se ejecuten simultáneamente, el efecto final que tomarán en la pila (precisamente la variable superior) se debe al orden del paso CAS (punto de linealización). Si podemos asegurarnos de que el punto de linealización realmente funciona, entonces la pila concurrente es correcta. La imagen del H_C es demasiado larga, pero podemos confirmar que debe haber un equivalente secuencial legal a H_C.
Entonces, si está implementando un objeto simultáneo, ¿cómo decir la exactitud de su programa? Debe identificar cada método de puntos de linealización y pensar cuidadosamente (o incluso probar) que siempre tendrán las invariantes del objeto concurrente. Entonces, el orden parcial de todas las llamadas a métodos se puede extender a, al menos, un orden total legal (historial secuencial de eventos) que cumpla con la especificación secuencial del objeto concurrente.
Entonces puede decir que su objeto simultáneo es el correcto.
Deberías echar un vistazo a los ejemplos en Wikipedia: http://en.wikipedia.org/wiki/Linearizability. –
Gracias a la página wiki también ha sido útil – unnknown
Vea la presentación de Mila Oren en http://www.cs.tau.ac.il/~afek/Mila.Linearizability.ppt. Ver también http://stackoverflow.com/questions/1375695/is-a-method-with-no-linearization-points-always-not-linearizable. El artículo de wikipedia es casi inútil. –