2010-03-01 16 views
6

Estoy escribiendo una aplicación C# Windows Form que procesa citas del mercado a través de un algoritmo (estrategia) para crear pedidos a una firma de corretaje. Todo parece estar probando bastante bien hasta que intenté construir la capacidad de ejecutar múltiples estrategias simultáneamente con cada estrategia en su propio hilo. En este punto, todo comienza a ejecutarse incorrectamente. Creo que tengo algunas clases que no son seguras para subprocesos y que generan un comportamiento errático. ¡Cualquier idea sobre cómo puedo enhebrar esto de una manera segura para los hilos es muy apreciada!Asegurando seguridad de subprocesos

La manera en que se envían las Cotizaciones a los Algoritmos es la siguiente: 1) Los Eventos de Datos de Mercado se activan desde el Software Brokers a una clase de cliente en mi Software llamado ConnectionStatus. Cuando se desencadena el evento Data del mercado, se crea un objeto Quote a partir de los valores actuales de estas variables estáticas que representan Bid, ask, etc. Una vez que se construye la cotización, me esfuerzo por enviarla a cada uno de los Algoritmos de Estrategia que están corriendo. Aquí está el código que estoy utilizando para hacer eso:

foreach (StrategyAssembler assembler in StrategyAssembleList.GetStrategies()) 
{     
    BackgroundWorker thread = strategyThreadPool.GetFreeThread(); 
    if (thread != null) 
    { 
     thread.DoWork += new DoWorkEventHandler(assembler.NewIncomingQuote); 
     thread.RunWorkerAsync(quote); 
    } 
} 

StrategyAssembler es una clase que crea una instancia de la clase StrategyManager que a su vez crea una instancia de la estrategia que contiene los algoritmos reales. Puede haber 4 o 6 instancias diferentes de StrategyAssembler, cada una de las cuales se ha agregado a una instancia de Singleton de StrategyAssembleList que es una BindingList.

El objeto de comillas entrantes se pasa al método NewIncomingQuote de la clase StrategyAssembler. Ese código es el siguiente:

public void NewIncomingQuote(object sender, DoWorkEventArgs e) 
    { 
     Quote QUOTE = e.Argument as Quote;    

     lock (QuoteLocker) 
     { 
      manager.LiveQuote(QUOTE); 

      priorQuote = QUOTE; 
     } 
    } 

Estaba pensando que mediante el uso de una cerradura antes de pasar a la cita en el método manager.LiveQuote (cita Cotización) que todos los objetos que utilizan la cita "aguas abajo" de este punto sería ser capaz de consumir la cotización de manera segura, pero las pruebas muestran lo contrario. ¿Hay alguna manera de poner cada instancia de StrategyAssembler en su propio hilo que asegure que todos los objetos creados por el Ensamblador de Estrategia sean seguros para hilos y luego alimentar el presupuesto en el Ensamblador de Estrategia? ¿Es esta forma de pensar una forma apropiada de lidiar con esta situación?

Gracias de antemano por cualquier comentario o ayuda,

aprendizaje1

+0

1) ¿Qué hace 'manager.LiveQuote()' do? ¿Es la interfaz para tus métodos de estrategia? 2) ¿'QuoteLocker' es un miembro de la instancia' StrategyAssembler'? 3) Finalmente, ¿qué tipo de comportamiento esperas y qué estás viendo? –

+0

1) gerente.Livequote() es el método que a) Agrega la cotización a una lista a la que hacen referencia Clases indicadoras y clase de algoritmo de estrategia y Clase de simulación de ordenFill b) Pasa la cotización en el algoritmo de estrategia c) Gestiona objetos de orden en la estrategia para simular el llenado de pedidos/Cancelación/latencia de comunicación, etc. – Learning1

+0

2) QuoteLocker es un objeto estático objeto estático QuoteLocker = new object(); – Learning1

Respuesta

1

El bloqueo debe producirse tanto en la lectura y la escritura a cualquier estado compartido. Sin bloqueos en la lectura, el código aún puede leer y escribir al mismo tiempo.

Puede ajustar el bloqueo de lectura y escritura en el administrador.

+0

Gracias por los comentarios gastados. ¿Estás diciendo que en cada punto "descendente" del código que está leyendo el objeto de cita necesito un candado? (Hay muchos lugares y objetos en sentido descendente que utilizan valores de cada cita). – Learning1

+0

Envolver bloqueos en el administrador es una forma peligrosa. Ejemplo. Si tiene una lectura (bloqueada) y una escritura (bloqueada), se siente seguro. Pero si haces una lectura bloqueada(), un incremento, una escritura bloqueada() aún tendrás una violación de tu sección crítica. El bloqueo debe estar alrededor del uso total de los datos, lo que resulta en una modificación atómica de sus datos. – Adriaan

+0

@spender En este caso, es mejor copiar la cita y evitar el período de bloqueo ... de hecho, un bloqueo adecuado haría que sus estrategias se ejecuten secuencialmente, hará que el esfuerzo de subprocesamiento múltiple sea completamente inútil Y más costoso desde el punto de vista computacional debido a la sobrecarga de bloqueo + cambio de contexto. – Kiril

0

Si:

1) Estrategias se invocan a través del método LiveQuote y puede modificar Quote casos.

2) Los cambios en las instancias Quote no se deben compartir entre las estrategias.

Debe crear una copia del Quote proporcionado antes de llamar al LiveQuote() y enviar la copia al método de estrategia en lugar de la cita original. Dependiendo de otros requisitos, es posible que no necesite ningún bloqueo.

0

Hay dos cosas que están sucediendo en su código:
1. Recibió una cita de un hilo (el productor también es el feed de datos del mercado).
2.Usted envía la cotización a otro hilo (el consumidor AKA StrategyAssembler).

En este punto hay una contención en la cita, en otras palabras, el hilo productor y cada hilo de consumidor (es decir, cada instancia de una estrategia) puede modificar la cita que acaba de proporcionar. Para poder eliminar la disputa, debe hacer una de estas tres cosas:

  1. Sincronizar entre todos los hilos con acceso a la cotización.
    O
  2. Haga que la cotización sea inmutable (y asegúrese de que el productor no la reemplace).
    O
  3. Dé a cada consumidor una copia de la cita.

Para su caso, le sugiero que tome la tercera opción porque bloquear es más caro que copiar una cotización (espero que sus presupuestos no sean muy grandes) ... la opción dos también es buena, pero su estrategia no debería modificar la cita

Al darle a cada consumidor una copia de la cita, se asegura de que no comparte ningún dato, por lo tanto, ningún otro hilo modificará la cotización y eliminará la disputa. Si sus estrategias no están creando otros hilos, entonces ya está.

En general se debe evitar el bloqueo y usted debe tratar de minimizar el intercambio de datos, pero si TIENE QUE compartir datos entre hilos, entonces debería hacerlo correctamente:
Para que sus estrategias para sincronizar correctamente, obligada sincronizar en el mismo objetoQuoteLocker, es decir, QuoteLocker debe estar visible para cada hilo. Incluso si lo haces correctamente y haces que tus estrategias se sincronicen (asegúrate de QuoteLocker) entonces también podrías no tener hilos ... estarás ejecutando la sobrecarga del cambio de contexto + bloqueo y tus estrategias se ejecutarán secuencialmente para el mismo citar.

de actualización por los comentarios: Si deja el código como está (lo que significa que usted proporcione una copia de la cotización para cada hilo), entonces no veo por qué sus otras estrategias no conseguirán la cita hasta que el se completa la primera estrategia ... su primera estrategia probablemente comenzará a funcionar mientras se crean los hilos para las otras estrategias. El objetivo de hacer que sus estrategias se ejecuten en un hilo separado es evitar precisamente ese problema ... inicia un nuevo hilo para que sus otras estrategias no estén esperando una a la otra para completarse.

Esta parte del código lo más probable es completa, incluso antes de que todos los hilos empiezan a trabajar ...

foreach (StrategyAssembler assembler in StrategyAssembleList.GetStrategies()) 
{     
    BackgroundWorker thread = strategyThreadPool.GetFreeThread(); 
    if (thread != null) 
    { 
     thread.DoWork += new DoWorkEventHandler(assembler.NewIncomingQuote); 
     Quote copy = CopyTheQuote(quote);// make an exact copy of the quote 
     thread.RunWorkerAsync(copy); 
    } 
} 

está cambiando su alimentación mercado de la cita real mientras se está creando los hilos? Los feeds de mercado generalmente brindan instantáneas, por lo tanto, a menos que algo esté cambiando su presupuesto mientras está haciendo los hilos, entonces el diseño anterior debería estar bien. Si hay un problema con el diseño, entonces puedo darle un diseño de productor y consumidor múltiple basado en una cola de bloqueo que también es muy eficiente (you can check out this discussion for an idea on how it works y puedo decirle cómo modificarlo para su ejemplo específico).

+0

Sí, prevenir es mejor que arreglar. Si sus hilos pueden ejecutarse de manera independiente (opción 3), tendrá la opción más segura y con mejor rendimiento. Como no puede garantizar que la primera cita se maneje por completo primero, es posible que necesite agregar una marca de tiempo (de alta resolución) en su copia. – Adriaan

+0

Gracias por la retroalimentación Lirik. Todo eso tiene mucho sentido. La opción tiene más sentido para mí por las razones que mencionaste y otras también. En términos de hacer una copia y pasar esto a cada instancia de ensamblador, creo que esto se haría mejor con un evento para que todos los ensambladores obtengan su copia de la cita simulaneamente y luego puedan procesar su lógica. De lo contrario, si se deja en el bucle, las siguientes estrategias no obtendrán la cita hasta que la primera complete su lógica (aproximadamente 100 ms y las comillas pueden llegar en tan solo 50 ms). – Learning1

+0

Si incluyo la cita en un evento que cada ensamblador se subscribe, ¿cuál es la mejor forma de que cada ensamblador se ejecute en un subproceso para poder aprovechar más núcleos en la CPU? Por el momento, cada uno de estos ensambladores y el algoritmo de estrategia que cada uno ha creado se ejecutan todos en el hilo principal – Learning1

Cuestiones relacionadas