2009-09-03 12 views
11

Tengo una función que invoca una solicitud de lectura o escritura en un puerto serie y luego devuelve el valor que se leyó. Estoy usando Commstudio Express (estoy implementando una clase de Commstudio), pero las características de tiempo de espera no parecen funcionar del todo, así que estoy tratando de implementar mi propio tiempo de espera. Actualmente tengo un temporizador que se configura a petición para leer o escribir en el puerto, y si el temporizador se apaga, la devolución de llamada cierra la conexión causando una excepción. Traté de hacer que la devolución de llamada del temporizador arrojara una excepción, pero la excepción necesita propagarse a través del hilo que estaba llamando a la función de lectura/escritura original, así que de esta manera, funciona, pero siento que está desordenado y debe ser una mejor manera de hacer lo que quiero.Implementación de un tiempo de espera en una función que devuelve un valor

+0

similares en http: // desbordamiento de pila.com/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

Respuesta

34

Aquí es una solución genérica que le permite envolver cualquier método en un tiempo de espera:

http://kossovsky.net/index.php/2009/07/csharp-how-to-limit-method-execution-time/

Utiliza la utilidad Thread.Join sobrecarga que acepta un tiempo de espera en milisegundos en lugar de utilizar manualmente temporizadores. La única cosa que haría de manera diferente es cambiar la bandera el éxito y el valor del resultado para que coincida con el patrón TryParse, de la siguiente manera:

public static T Execute<T>(Func<T> func, int timeout) 
{ 
    T result; 
    TryExecute(func, timeout, out result); 
    return result; 
} 

public static bool TryExecute<T>(Func<T> func, int timeout, out T result) 
{ 
    var t = default(T); 
    var thread = new Thread(() => t = func()); 
    thread.Start(); 
    var completed = thread.Join(timeout); 
    if (!completed) thread.Abort(); 
    result = t; 
    return completed; 
} 

Y así es como se lo utilizaría:

var func = new Func<string>(() => 
    { 
     Thread.Sleep(200); 
     return "success"; 
    }); 
string result; 
Debug.Assert(!TryExecute(func, 100, out result)); 
Debug.Assert(result == null); 
Debug.Assert(TryExecute(func, 300, out result)); 
Debug.Assert(result == "success"); 

También podría agregue sobrecargas que acepten Action en lugar de Func si desea ejecutar un método que no devuelve un valor.

+0

¿Qué tal este http://stackoverflow.com/a/990566/206730? Más información en http://stackoverflow.com/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

+1

** Importante: ** before 'thread.Start()' de lo contrario, debería establecer 'thread.IsBackground = true' el hilo continúa ejecutándose después del tiempo de espera, y después de cerrar la aplicación, seguirá ejecutándose en el Administrador de tareas. –

+0

@ user2270404 El hilo se cancela dos líneas de código después de que se inicia. A menos que el hilo atrape la excepción ThreadAbortException y llame a 'ResetAbort', o algo arroje una excepción, no veo cómo el hilo podría seguir funcionando. Dicho esto, al llamar a 'Join' con un tiempo de espera inferior a -1 ** se ** [lanzar una excepción] (http://msdn.microsoft.com/en-us/library/6b1kkss0.aspx), entonces cualquiera copiar este código debería validar la entrada (y si eso no parece suficiente, configure 'IsBackground' también). –

2

Parece que estás haciendo un bloqueo de lectura/escritura. Lo que quiere hacer es una lectura/escritura sin bloqueo.

es probable que haya una manera de contar el puerto COM que está queriendo no bloqueo.

¿Estás seguro de que los tiempos de espera no están trabajando con commstudio? tal vez tengas que hacer algo especial para inicializarlos.

En cualquier caso, que desea leer tantos datos como sea posible y si no hay ninguna disponible intervalo de tiempo (dependiendo de lo que el valor del tiempo de espera es). Querrá seguir haciendo bucles sin datos disponibles y sin errores, y luego devolver una condición de tiempo de espera si no hubiera nada disponible.

Haga que su función de lectura devuelva un número entero. valores negativos = valor de error, p. ej. -1 = tiempo de espera, número positivo de bytes leídos ... al menos esa es la forma en que lo haría.

+0

Estoy bastante seguro de que el tiempo de espera no funciona, el evento DeviceError nunca se levanta y no pasa nada, incluso dejándolo funcionar durante una hora. Mi función de lectura es una sobrecarga simple que agrega un poco de registro personalizado, pero al final, es una base. Leer(). – MGSoto

0

Para el puerto com que sólo podría probar si hay algo disponible y luego hacer una lectura en vez de hacer un bloqueo de lectura sin saber si hay algo todavía. Algo así como:

Int32 timeout=1000; 
String result = String.Empty'; 
while (timeout!=0) { 
    if (Serial.BytesToRead>0) { 
    while (Serial.BytesToRead>0) { 
     result+=Serial.ReadChar(); 
    } 
    break; 
    } 
    Thread.Sleep(1); 
    timeout--; 
} 
0

En caso de que alguien quiere hacer esto en VB.Net, no escuchar a los que dicen que no se puede hacer! Puede necesitar modificar sus parámetros genéricos para adaptarse a su caso de uso.

Public Shared Function Execute(Of I, R)(Func As Func(Of I, R), Input As I, TimeOut As Integer) As R 
    Dim Result As R 
    TryExecute(Func, Input, TimeOut, Result) 
    Return Result 
    End Function 

    Public Shared Function TryExecute(Of I, R)(Func As Func(Of I, R), Input As I, TimeOut As Integer, ByRef Result As R) As Boolean 
    Dim OutParam As R = Nothing 
    Dim Thread As New System.Threading.Thread(Sub() InlineAssignHelper(OutParam, Func(Input))) 
    Thread.IsBackground = True 
    Thread.Start() 
    Dim Completed As Boolean = Thread.Join(TimeOut) 
    If Not Completed Then Thread.Abort() 
    Result = OutParam 
    Return Completed 
    End Function 

    Private Shared Function InlineAssignHelper(Of T)(ByRef Target As T, ByVal Value As T) As T 
    Target = Value 
    Return Value 
    End Function 

Y un ejemplo de cómo usarla (la mía era con Regex.Match, que a veces se apaga en la tierra de nunca jamás si los patrones contiene demasiados comodines:

Public Function Match(Input As String) As Match 
    If Regex Is Nothing Then Return Nothing 
    Dim RegexMatch As System.Text.RegularExpressions.Match = Nothing 
    Dim Func As New Func(Of String, System.Text.RegularExpressions.Match)(Function(x As String) Regex.Match(x)) 
    If Runtime.TryExecute(Of String, System.Text.RegularExpressions.Match)(Func, Input, 2000, RegexMatch) Then 
     Return (New Match(Me, Regex.Match(Input), Input)) 
    Else 
     Return Nothing 
    End If 
    End Function 
Cuestiones relacionadas