Acceso a un controlador desde otro hilo ?

Iniciado por kondrag_X1, 3 Abril 2015, 16:11 PM

0 Miembros y 1 Visitante están viendo este tema.

kondrag_X1

Buenas ya  se que esta pregunta es genérica y básica pero yo quiero centrarme en la eficiencia y cual es más fácil de entender cuando se relee el código.

en este link podeis encontrar las diferencias:
http://www.elguille.info/NET/vs2005/trucos/acceder_a_un_control_desde_otro_hilo.htm

mi cuestión es que he descubierto otra forma y me gustaría una opinion a ver cual les parece más clara y si sabrían decirme a nivel de eficiencia cual es la mejor.

mi forma


string mensaje = "";
            if(InvokeRequired)
            {
                Invoke(new Action(() => mensaje += combobox.SelectedItem));
             }

como se ve en este caso se utiliza una función lamba.

Eleкtro

#1
¿A que diferencia te refieres exactamente?.

En ambos códigos el tuyo y el de aquella url se llama al mismo método 'Windows.Forms.Control.InvokeRequired' y 'Windows.Forms.Control.Invoke', le pasas el delegado y al compilar se generan las mismas instrucciones IL que internamente determinan lo mismo, hallar el handle del control/form para descubrir a que thread pertenece.

No hay diferencia alguna en lo que se refiere a eficiencia, tampoco por que le pases un delegado con la referencia del método o una expresión lambda.

Ahora, deberías seguir los ejemplos de buenos hábitos, es decir, invocar el control y no el form (aunque eso no afecta para nada al rendimiento pero puede resultar en confusión), y ademas añadir una verificación antes de invocarlo:

Ejemplo en VB.Net
Código (vbnet) [Seleccionar]
Dim msg As String = "" & ComboBox1.SelectedItem() ' No es necesario invocar para leer.

Select Case Me.TextBox1.InvokeRequired

 Case True ' Invoco para modificar la propiedad.
     Me.TextBox1.Invoke(Sub() Me.TextBox1.Text = msg)

 Case Else
     Me.TextBox1.Text = msg

End Select


Traducción online a C#:
Código (csharp) [Seleccionar]
string msg = "" + ComboBox1.SelectedItem(); // No es necesario invocar para leer.

switch (this.TextBox1.InvokeRequired) {

case true: // Invoco para modificar la propiedad.
this.TextBox1.Invoke(() => this.TextBox1.Text == msg);
break;

default:
this.TextBox1.Text = msg;
break;
}


Si quieres reducir el código para no tener que estar escribiendo siempre lo mismo, puedes desarrollar un método genérico cómo este:

Código (vbnet) [Seleccionar]
   ''' <remarks>
   ''' *****************************************************************
   ''' Snippet Title: Control Invoker
   ''' Code's Author: Elektro
   ''' Date Modified: 03-April-2015
   ''' Usage Example:
   ''' ControlInvoker(TextBox1, Sub(tb) tb.Text = "Hello World!")
   '''
   ''' ControlInvoker(TextBox1, Sub(tb As TextBox)
   '''                              For x As Integer = 0 To 5
   '''                                  tb.AppendText(CStr(x))
   '''                              Next x
   '''                          End Sub)
   ''' *****************************************************************
   ''' </remarks>
   ''' <summary>
   ''' Executes an encapsulated method on the thread that owns the specified control.
   ''' </summary>
   ''' <typeparam name="T"></typeparam>
   ''' <param name="ctrl">The control to invoke.</param>
   ''' <param name="method">The encapsulated method to be called.</param>
   Public Sub ControlInvoker(Of T As Control)(ByVal ctrl As T, ByVal method As Action(Of T))

       If ctrl.InvokeRequired Then
           ctrl.Invoke(New Action(Of T, Action(Of T))(AddressOf ControlInvoker), ctrl, method)

       Else
           method(ctrl)

       End If

   End Sub


Traducción online a C#:
Código (csharp) [Seleccionar]
/// <remarks>
/// *****************************************************************
/// Snippet Title: Control Invoker
/// Code's Author: Elektro
/// Date Modified: 03-April-2015
/// Usage Example:
/// ControlInvoker(TextBox1, Sub(tb) tb.Text = "Hello World!")
///
/// ControlInvoker(TextBox1, Sub(tb As TextBox)
///                              For x As Integer = 0 To 5
///                                  tb.AppendText(CStr(x))
///                              Next x
///                          End Sub)
/// *****************************************************************
/// </remarks>
/// <summary>
/// Executes an encapsulated method on the thread that owns the specified control.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ctrl">The control to invoke.</param>
/// <param name="method">The encapsulated method to be called.</param>

public void ControlInvoker<T>(T ctrl, Action<T> method) where T : Control
{
if (ctrl.InvokeRequired) {
ctrl.Invoke(new Action<T, Action<T>>(ControlInvoker), ctrl, method);

} else {
method(ctrl);

}

}


Ejemplos de uso en VB.Net
Código (vbnet) [Seleccionar]
ControlInvoker(TextBox1, Sub(tb) tb.Text = "Hello World!")

Código (vbnet) [Seleccionar]
ControlInvoker(TextBox1, Sub(tb As TextBox)
                         For x As Integer = 0 To 5
                             tb.AppendText(CStr(x))
                         Next x
                     End Sub)


Traducción online a C#:
Código (csharp) [Seleccionar]
ControlInvoker(TextBox1, tb => tb.Text == "Hello World!");

Código (csharp) [Seleccionar]
ControlInvoker(TextBox1, (TextBox tb) =>
{
for (int x = 0; x <= 5; x++) {
tb.AppendText(Convert.ToString(x));
}
})


Saludos.








kondrag_X1

Hola,

muchísimas gracias por tu respuesta, si no entiendo mal la clase Invokerequired e Invoke es lo mismo. Mi pregunta ahora es: El hecho de usar delegados para esta tarea facilita la compresensión del código? Es decir, de los dos métodos el de la página y el mío ¿Cual te costaría menos entender?

saludos.

Eleкtro

#3
Cita de: kondrag_X1 en  5 Abril 2015, 17:09 PMsi no entiendo mal la clase Invokerequired e Invoke es lo mismo.
Para nada, quizás me interpretaste mal, la propiedad InvokeRequired simplemente determina si es necesario llamar al método Invoke (éste comprueba si el control se encuentra en un hilo diferente al actual), y el método Invoke hace el resto, realiza la invocación.

Si invocas repetidamente el control desde el hilo que lo creó sin realizar el chequeo con InvokeRequired entonces producirá una (muy ínfima) disminuición de rendimiento.

Si estás seguro de que el control siempre va a ser invocado desde un hilo distinto al que lo creó entones no es necesario realizar el chequeo con InvokeRequired pero tampoco está de más conservar los buenos hábitos añadiendo el chequeo.

Cita de: kondrag_X1 en  5 Abril 2015, 17:09 PMMi pregunta ahora es: El hecho de usar delegados para esta tarea facilita la compresensión del código? Es decir, de los dos métodos el de la página y el mío ¿Cual te costaría menos entender?

Hombre... creo que la comprensión del código depende de las circunstancias y de los gustos de cada uno.

Pongo cómo ejemplo este código:
sub()
   Me.TextBox1.Invoke(Sub() Me.TextBox1.Text = msg)
end sub


En mi opinión no es muy correcto tener un método encapsulado en otro método, pero usar una expresión lamdba ahí a mi me resulta más comprensible que crear el delegado y defnir un método adicional en otra parte del código, por que solamente estoy modificando una propiedad en ese ejemplo y además se que no hay lugar a ninguna posible excepción cross-thread o de otro tipo.

Al invocar, yo utilizaría los delegados para reemplazar a una expresión lambda en caso de que tuviese que realizar varias operaciones o en caso de que no estuviese del todo seguro de si pueden producirse excepciones entonces las controlaría en el método del delegado en lugar de en el bloque del lambda por que demasiadas isntrucciones quedaría bastante feo y además el lambda es un método anónimo.

Saludos!