2009-02-09 16 views

Respuesta

31

El problema es diseñar una colección segura para subprocesos no es simple. Claro que es lo suficientemente simple como para diseñar una colección que se puede modificar/leer de múltiples hilos sin corromper el estado. Pero es mucho más difícil diseñar una colección que se pueda usar dado que se actualiza a partir de múltiples hilos. Tome el siguiente código como un ejemplo.

if (myCollection.Count > 0) { 
    var x = myCollection[0]; 
} 

Supongamos que myCollection es una colección segura para la secuencia de comandos donde las adiciones y actualizaciones garantizan la no alteración del estado. Este código no es seguro para subprocesos y es una condición de carrera.

¿Por qué? Aunque myCollection es seguro, no hay garantía de que no se produzca un cambio entre las dos llamadas a myCollection: countly named y el indexador. Otro hilo puede entrar y eliminar todos los elementos entre estas llamadas.

Este tipo de problema hace que el uso de una colección de este tipo sea francamente una pesadilla. Nunca puede permitir que el valor de retorno de una llamada influya en una llamada posterior en la colección.

EDITAR

amplié esta discusión en un reciente post: http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

+0

Tengo exactamente el mismo problema. ¿Cómo uso Dispatcher o si no agrego un elemento a mi BindingList desde un hilo de fondo? – Houman

+0

Sí, en resumen, el diseño de .NET collection * interfaces * no es apropiado para la seguridad de subprocesos. Como señala Jared, por ejemplo, la propiedad Count es inútil en un entorno de subprocesos múltiples. –

+0

Hay muchos subconjuntos útiles de la funcionalidad 'IList ' que podrían implementarse de manera segura para hilos. Los elementos que eliminan cosas o mueven cosas serían problemáticos, pero el resto de la interfaz sería un subconjunto útil para muchas aplicaciones. Sería útil una versión de 'Add' que informara el índice del elemento agregado, pero no todas las aplicaciones lo necesitarían. Si cada elemento que se agrega continuará existiendo en la misma ranura durante la vida útil de la lista, una implementación 'IList ' sin hilos podría ser útil en muchos escenarios de subprocesos múltiples sin bloqueo externo. – supercat

6

Para añadir un poco a la excelente respuesta de Jared: hilo de seguridad no viene de forma gratuita. Muchas (¿la mayoría?) Colecciones solo se usan en un solo hilo. ¿Por qué esas colecciones tienen penalizaciones de rendimiento o funcionalidad para hacer frente al caso de subprocesos múltiples?

+0

Bueno, tal vez debería volver a expresarlo entonces: ¿por qué la estructura no proporciona una ThreadSafeObservableCollection o algo así? –

+0

Esa es una pregunta más razonable, pero luego la respuesta de Jared entra en acción. Realmente depende de lo que quiere decir con "hilo seguro", que no es un simple indicador de sí/no. –

2

Si quiere volverse loco - here's a ThreadedBindingList<T> que hace que las notificaciones vuelvan a aparecer automáticamente en el hilo de la interfaz de usuario. Sin embargo, todavía sería seguro para un hilo hacer actualizaciones, etc. a la vez.

+0

Esta implementación solo calcula 'agrega' a la cadena de contexto de sincronización: no protege contra un número de otras condiciones de carrera y/o errores modificados de colección al enumerar la lista. – piers7

5

La recopilación de las ideas de todas las otras respuestas, creo que esta es la forma más sencilla de resolver sus problemas:

cambiar la pregunta de: "¿Por qué no es la clase X cuerdo"

a

"¿Cuál es la manera sensata de hacer esto con la clase X?"

  1. en el constructor de la clase, obtener el displatcher actual a medida que crea sus colecciones observables. Porque, como ha señalado, la modificación necesaria para debe realizarse en el original, que puede no ser el principal hilo GUI. Así que App.Current.El despachador no es el correcto, y no todas las clases tienen un this.Dispatcher.

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; 
    _data = new ObservableCollection<MyDataItemClass>(); 
    
  2. Utilice el despachador para invocar sus secciones de código que necesita el hilo original.

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); })); 
    

Que debe hacer el truco para usted. Aunque hay situaciones, puede preferir . BeginInvoke en lugar de . Invoque.

Cuestiones relacionadas