c# - saber - ig analytics
InvalidOperationException-el objeto está actualmente en uso en otro lugar-cruz roja (4)
Creo que esto es un problema de subprocesamiento múltiple. Use la regla de oro de Windows y actualice el panel en el panel de uso del subproceso principal. Invocar Esto debería superar la excepción de subprocesos cruzados
Tengo una aplicación de escritorio C # en la que un hilo que creo continuamente obtiene una imagen de una fuente (en realidad es una cámara digital) y la coloca en un panel (panel.Image = img) en la GUI (que debe ser otro hilo como es el código subyacente de un control.
La aplicación funciona, pero en algunas máquinas recibo el siguiente error en intervalos de tiempo aleatorios (impredecibles)
************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere.
Luego, el panel se convierte en una cruz roja, X roja: creo que este es el ícono de imagen no válida que se puede editar desde las propiedades. La aplicación sigue funcionando pero el panel nunca se actualiza.
Por lo que puedo decir, este error proviene del evento onpaint del control donde dibujo algo más en la imagen.
Intenté usar un candado allí pero no tuve suerte :(
La forma en que llamo a la función que coloca la imagen en el panel es la siguiente:
if (this.ReceivedFrame != null)
{
Delegate[] clients = this.ReceivedFrame.GetInvocationList();
foreach (Delegate del in clients)
{
try
{
del.DynamicInvoke(new object[] { this,
new StreamEventArgs(frame)} );
}
catch { }
}
}
este es el delegado
public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
public event ReceivedFrameEventHandler ReceivedFrame;
y así es como se registra la función dentro del código de control detrás:
Camera.ReceivedFrame +=
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);
También intenté
del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });
en lugar de
del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });
pero sin suerte
¿Alguien sabe cómo puedo solucionar este error o al menos detectar el error de alguna manera y hacer que el hilo ponga las imágenes en el panel una vez más?
Esto es porque la clase Gdi + Image no es segura para subprocesos. Sin embargo, puede evitar InvalidOperationException utilizando el bloqueo cada vez que necesite acceso a la imagen, por ejemplo para pintar o obtener el tamaño de la imagen:
Image DummyImage;
// Paint
lock (DummyImage)
e.Graphics.DrawImage(DummyImage, 10, 10);
// Access Image properties
Size ImageSize;
lock (DummyImage)
ImageSize = DummyImage.Size;
Por cierto, la invocación no es necesaria, si usará el patrón anterior.
Me parece que el mismo objeto de cámara se usa varias veces.
Por ejemplo, intente utilizar un nuevo búfer para cada fotograma recibido. Me parece que, mientras el cuadro de imagen está dibujando el nuevo marco, tu biblioteca de captura llena ese búfer nuevamente. Por lo tanto, en máquinas más rápidas esto podría no ser un problema, con máquinas más lentas podría ser un problema.
Una vez programé algo similar, después de cada trama recibida, tuvimos que solicitar recibir la siguiente trama y configurar el NUEVO buffer de recepción de trama en esa solicitud.
Si no puede hacer eso, copie el marco recibido de la cámara primero en un búfer nuevo y agregue ese búfer a una cola, o simplemente use 2 búferes alternos y verifique los excesos . Utilice myOutPutPanel.BeginInvoke para llamar al método camera_ReceivedFrame, o mejor ejecute un subproceso, que comprueba la cola, cuando tiene una nueva entrada, llama mnyOutPutPanel.BeginInvoke para invocar su método para establecer el nuevo búfer como imagen en el panel.
Además, una vez que recibió el búfer, use el Método de invocación de panel para invocar la configuración de la imagen (garantice que se ejecute en el hilo de la ventana y no en el hilo de su biblioteca de captura).
El siguiente ejemplo se puede llamar desde cualquier subproceso (biblioteca de captura u otro subproceso separado):
void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
if(myOutputPanel.InvokeRequired)
{
myOutPutPanel.BeginInvoke(
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame),
sender,
e);
}
else
{
myOutPutPanel.Image = e.Image;
}
}
Tuve un problema similar con el mismo mensaje de error, pero por más que lo intenté, bloquear el mapa de bits no me solucionó nada. Entonces me di cuenta de que estaba dibujando una forma con un pincel estático. Efectivamente, fue el cepillo lo que estaba causando la disputa del hilo.
var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
e.Graphics.FillRectangle(brush, location);
Esto funcionó para mi caso y la lección aprendida: compruebe todos los tipos de referencia que se utilizan en el punto donde se produce la contención de subprocesos.