2009-09-03 6 views
11

Ho una funzione che richiama una richiesta di lettura o scrittura su una porta seriale e quindi restituisce il valore letto. Sto usando Commstudio Express (sto implementando una classe da Commstudio), ma le funzionalità di timeout non sembrano funzionare affatto, quindi sto cercando di implementare il mio timeout. Attualmente ho un timer che viene impostato su richiesta per leggere o scrivere sulla porta, e se il timer si spegne, il callback chiude la connessione causando un'eccezione. Ho cercato di fare in modo che la richiamata del timer generasse un'eccezione, ma l'eccezione deve essere propagata attraverso il thread che stava chiamando la funzione originale di lettura/scrittura, quindi in questo modo funziona, ma mi sembra che sia disordinato e deve essere un modo migliore per fare ciò che voglio.Implementazione di un timeout su una funzione che restituisce un valore

+0

Simile a http: // StackOverflow.it/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

risposta

34

Ecco una soluzione generica che permette di avvolgere qualsiasi metodo in un timeout:

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

Esso utilizza l'utile Thread.Join overload che accetta un timeout in millisecondi piuttosto che manualmente utilizzando i timer. L'unica cosa che vorrei fare in modo diverso è di swap la bandiera successo e il valore del risultato per abbinare il modello TryParse, come segue:

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

Ed è così che si dovrebbe utilizzare:

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

Si potrebbe anche aggiungere sovraccarichi che accettano Action anziché Func se si desidera eseguire un metodo che non restituisce un valore.

+0

Che dire di questo http://stackoverflow.com/a/990566/206730? Altro in http://stackoverflow.com/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

+1

** Importante: ** prima di 'thread.Start()', si dovrebbe impostare 'thread.IsBackground = true' altrimenti il thread continua a funzionare dopo il timeout e, dopo aver chiuso l'applicazione, rimarrà in esecuzione in Task Manager. –

+0

@ user2270404 Il thread ottiene due righe di codice interrotte dopo l'avvio. A meno che il thread non rilevi l'eccezione ThreadAbortException e invii "ResetAbort', o qualcosa generi un'eccezione, non vedo come il thread possa continuare a funzionare. Detto questo, chiamare 'Join' con un timeout inferiore a -1 ** farà ** [lanciare un'eccezione] (http://msdn.microsoft.com/en-us/library/6b1kkss0.aspx), quindi nessuno la copia di questo codice dovrebbe convalidare l'input (e se non sembra abbastanza impostato anche su 'IsBackground'). –

2

Sembra che tu stia eseguendo un blocco di lettura/scrittura. Quello che vuoi fare è una lettura/scrittura non bloccante.

Probabilmente esiste un modo per comunicare alla porta com che non si desidera bloccare.

Sei sicuro che i timeout non funzionano con commstudio? forse devi fare qualcosa di speciale per inizializzarli.

In ogni caso, si desidera leggere quanti più dati possibile e se nessuno è disponibile a scadenza (a seconda del valore del timeout). Avrai voglia di continuare il ciclo senza dati disponibili e nessun errore e quindi restituire una condizione di timeout se non c'era nulla disponibile.

Fare in modo che la funzione di lettura restituisca un numero intero. valori negativi = valore errore ad es. -1 = timeout, numero positivo di byte letti ... almeno questo è il modo in cui lo farei.

+0

Sono abbastanza sicuro che il timeout non funzioni, l'evento DeviceError non viene mai generato e non accade nulla, lasciandolo funzionare per un'ora. La mia funzione di lettura è un semplice sovraccarico che aggiunge alcune registrazioni personalizzate, ma alla fine è una base. Leggi(). – MGSoto

0

Per il comport si potrebbe semplicemente verificare se c'è qualcosa disponibile e quindi fare una lettura invece di fare una lettura bloccata senza sapere che c'è ancora qualcosa. Qualcosa di simile:

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

Nel caso in cui qualcuno vuole fare questo in VB.Net, non ascoltare quelli che dicono che non si può fare! Potrebbe essere necessario modificare i parametri generici in base al proprio caso d'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 

e un esempio di come usarlo (la mia era con Regex.Match, che a volte si spegne in Never Never Land se i modelli contiene troppi jolly:

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 
Problemi correlati