2009-09-03 7 views
11

J'ai une fonction qui appelle une demande de lecture ou d'écriture sur un port série, puis renvoie la valeur qui a été lue. J'utilise Commstudio express (j'implémente une classe de Commstudio), mais ses fonctionnalités de timeout ne semblent pas fonctionner du tout, donc j'essaye d'implémenter mon propre timeout. Actuellement, j'ai une minuterie qui est définie sur demande pour lire ou écrire sur le port, et si le minuteur se déclenche, le rappel ferme la connexion provoquant une exception. J'ai essayé d'avoir le rappel de la minuterie jeter une exception, mais l'exception doit être propagée à travers le thread qui appelait la fonction de lecture/écriture originale, de cette façon, cela fonctionne, mais je me sens comme c'est désordonné et là doit être une meilleure façon de faire ce que je veux.Implémentation d'un délai sur une fonction renvoyant une valeur

+0

similaires à http: // stackoverflow.com/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

Répondre

34

Voici une solution générique qui vous permet d'envelopper toute méthode dans un délai d'attente:

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

Il utilise la surcharge Thread.Join utile qui accepte un délai d'attente en millisecondes plutôt que d'utiliser manuellement minuteries. La seule chose que je voudrais faire est différemment échange le drapeau de la réussite et de la valeur de résultat pour correspondre à la structure TryParse, comme suit:

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; 
} 

Et voici comment vous l'utiliser:

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"); 

Vous pouvez également ajoutez des surcharges qui acceptent Action au lieu de Func si vous voulez exécuter une méthode qui ne renvoie pas de valeur.

+0

Qu'en est-il de ce http://stackoverflow.com/a/990566/206730? Plus dans http://stackoverflow.com/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

+1

** Important: ** avant 'thread.Start()', vous devriez régler 'thread.IsBackground = true' sinon le thread continue de s'exécuter après expiration du délai, et après la fermeture de l'application, il restera en cours d'exécution dans le Gestionnaire des tâches. –

+0

@ user2270404 Le thread obtient avorté deux lignes de code après son démarrage. À moins que le thread intercepte l'exception ThreadAbortException et appelle 'ResetAbort', ou que quelque chose déclenche une exception, je ne vois pas comment le thread pourrait continuer à fonctionner. Cela dit, appeler 'Join' avec un timeout inférieur à -1 ** entraînera ** [throw a exception] (http://msdn.microsoft.com/fr-fr/library/6b1kkss0.aspx), donc tout le monde copier ce code devrait valider l'entrée (et si cela ne semble pas suffisant, placez 'IsBackground' aussi). –

2

On dirait que vous faites une lecture/écriture bloquante. Ce que vous voulez faire est une lecture/écriture non bloquante.

Il existe probablement un moyen d'indiquer au port de communication que vous souhaitez ne pas bloquer. Etes-vous sûr que les délais ne fonctionnent pas avec commstudio?

peut-être que vous devez faire quelque chose de spécial pour les initialiser. Dans tous les cas, vous souhaitez lire autant de données que possible et, si aucune n'est disponible, le délai d'attente (en fonction de la valeur de la temporisation). Vous voudrez continuer à boucler alors qu'aucune donnée n'est disponible et aucune erreur, puis renvoyer une condition de temporisation s'il n'y avait rien de disponible.

Faites que votre fonction de lecture renvoie un nombre entier. valeurs négatives = valeur d'erreur, par ex. -1 = timeout, nombre d'octets lus ... au moins c'est comme ça que je le ferais.

+0

Je suis presque sûr que le timeout ne fonctionne pas, l'évènement DeviceError n'est jamais déclenché et rien ne se passe même en le laissant tourner pendant une heure. Ma fonction de lecture est une surcharge simple qui ajoute une journalisation personnalisée, mais à la fin, c'est une base.Read(). – MGSoto

0

Pour le comportement, vous pouvez simplement tester s'il y a quelque chose de disponible et ensuite faire une lecture au lieu de faire une lecture bloquante sans savoir qu'il y a quelque chose pour le moment. Quelque chose comme:

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 cas où quelqu'un veut faire en VB.Net, ne pas écouter ceux qui disent qu'il ne peut pas être fait! Vous devrez peut-être modifier vos paramètres génériques en fonction de votre cas d'utilisation.

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 

Et un exemple de comment l'utiliser (la mienne était avec Regex.Match, qui va parfois loin dans la terre ne jamais si les motifs contient trop de cartes sauvages:

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