La distinción principal, como señala en su pregunta, es si el programador alguna vez se adelantará a un hilo. La forma en que un programador piensa en compartir estructuras de datos o en sincronizar entre "hilos" es muy diferente en sistemas preventivos y cooperativos.
En un sistema cooperativo (que va por muchos nombres, de cooperación multi-tarea , nonpreemptive multitarea , las discusiones a nivel de usuario , hilos verdes , y las fibras son cinco de las más comunes en la actualidad) que el programador está garantizado que su código se ejecutará de forma atómica , siempre y cuando No hacen llamadas ni llamadas al sistema yield()
. Esto hace que sea particularmente fácil tratar con estructuras de datos compartidas entre múltiples fibras. A menos que necesite realizar una llamada al sistema como parte de una sección crítica, no es necesario marcar las secciones críticas (con mutex lock
y unlock
llamadas, por ejemplo). Entonces en código como:
x = x + y
y = 2 * x
el programador no necesita preocuparse de que alguna otra fibra pueda estar trabajando con las variables x
y y
al mismo tiempo. x
y y
se actualizarán juntos atómicamente desde la perspectiva de todas las otras fibras. Del mismo modo, todas las fibras podrían compartir una estructura más complicada, como un árbol y una llamada similar tree.insert(key, value)
no necesitaría estar protegida por ningún mutex o sección crítica.
Por el contrario, en un sistema de subprocesamiento múltiple preventivo, como con subprocesos verdaderamente paralelos / multinúcleo, todas las posibles intercalaciones de instrucciones entre subprocesos son posibles a menos que haya secciones críticas explícitas. Una interrupción y una preferencia podrían convertirse entre dos instrucciones. En el ejemplo anterior:
thread 0 thread 1
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
read y
< thread 1 could read or modify x or y at this point
add x and y
< thread 1 could read or modify x or y at this point
write the result back into x
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
multiply by 2
< thread 1 could read or modify x or y at this point
write the result back into y
< thread 1 could read or modify x or y at this point
Entonces, para ser correcto en un sistema preventivo, o en un sistema con hilos verdaderamente paralelos, debe rodear cada sección crítica con algún tipo de sincronización, como un mutex lock
al principio y un mutex unlock
al final.
Por lo tanto, las fibras son más similares a las bibliotecas de E / S asíncronas que a los hilos preventivos o hilos verdaderamente paralelos. Se invoca el planificador de fibra y puede cambiar fibras durante operaciones de E / S de latencia larga. Esto puede brindar el beneficio de múltiples operaciones simultáneas de E / S sin requerir operaciones de sincronización alrededor de secciones críticas. Por lo tanto, el uso de fibras puede, quizás, tener menos complejidad de programación que los hilos preventivos o verdaderamente paralelos, pero la falta de sincronización alrededor de las secciones críticas conduciría a resultados desastrosos si intentara ejecutar las fibras de manera realmente simultánea o preventiva.