2009-09-03 13 views
11

Mam funkcję, która wywołuje żądanie odczytu lub zapisu na porcie szeregowym, a następnie zwraca wartość, która została odczytana. Używam programu Commstudio Express (implementuję klasę z Commstudio), ale funkcje czasu oczekiwania w ogóle nie działają, więc próbuję wprowadzić własny limit czasu. Obecnie mam timer ustawiony na żądanie odczytu lub zapisu do portu, a jeśli timer się wyłączy, oddzwanianie zamyka połączenie powodując wyjątek. Próbowałem, aby wywołanie zwrotne zegara rzuciło wyjątek, ale wyjątek musi zostać rozpropagowany przez wątek, który wywoływał oryginalną funkcję odczytu/zapisu, więc w ten sposób działa, ale mam wrażenie, że jest niechlujny i musi być lepszym sposobem robienia tego, co chcę.Implementacja limitu czasu na funkcji zwracającej wartość

+0

Podobny w http: // przepełnienie stosu.com/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

Odpowiedz

34

Oto ogólnym rozwiązanie pozwala owinąć dowolną metodą timeout:

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

wykorzystuje użyteczne Thread.Join przeciążenia, który akceptuje limitu czasu w milisekundach, a nie ręcznie, za pomocą liczników. Jedyną rzeczą, którą chciałbym zrobić inaczej jest zamiana flagi sukces i wartość wynik dopasować wzór TryParse, co następuje:

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

I to jak byś go używać:

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

Można też dodaj przeciążenia, które akceptują Action zamiast Func, jeśli chcesz wykonać metodę, która nie zwraca wartości.

+0

Co z tym http://stackoverflow.com/a/990566/206730? Więcej w http://stackoverflow.com/questions/299198/implement-c-sharp-generic-timeout – Kiquenet

+1

** Ważne: ** przed 'thread.Start()', powinieneś ustawić 'thread.IsBackground = true' inaczej wątek nadal działa po przekroczeniu limitu czasu, a po zamknięciu aplikacji pozostanie uruchomiony w Menedżerze zadań. –

+0

@ user2270404 Wątek jest przerywany po dwóch wierszach kodu po jego uruchomieniu. O ile wątek nie chwyta wyjątku ThreadAbortException i wywołuje "ResetAbort" lub coś wyrzuca wyjątek, nie widzę sposobu, w jaki wątek mógłby nadal działać. To powiedziawszy, wywołanie 'Join' z limitem czasu mniejszym niż -1 ** spowoduje ** [zgłoszenie wyjątku] (http://msdn.microsoft.com/en-us/library/6b1kkss0.aspx), więc ktokolwiek skopiowanie tego kodu powinno sprawdzić poprawność danych wejściowych (a jeśli to nie wystarczy, to ustaw również "IsBackground"). –

2

Wygląda na to, że robisz blokujący odczyt/zapis. To, co chcesz zrobić, to nie blokować odczytu/zapisu.

Prawdopodobnie jest sposób, aby powiedzieć, że port COM nie jest blokowany.

Czy jesteś pewien, że limity czasu nie działają z commstudio? może musisz zrobić coś specjalnego, aby je zainicjować.

W każdym przypadku chcesz przeczytać jak najwięcej danych i jeśli nie ma czasu na wyjście (w zależności od wartości limitu czasu). Będziesz chciał zachować pętlę, gdy żadne dane nie będą dostępne i nie będzie błędu, a następnie zwróci warunek czasu, jeśli nie było nic dostępnego.

Spraw, aby funkcja odczytu zwracała liczbę całkowitą. wartości ujemne = wartość błędu np. -1 = timeout, dodatnia liczba bajtów przeczytanych ... przynajmniej tak bym to zrobił.

+0

Jestem prawie pewien, że limit czasu nie działa, zdarzenie DeviceError nigdy nie jest wywoływane i nic się nie dzieje, nawet pozostawiając to na godzinę. Moja funkcja odczytu jest prostym przeciążeniem, które dodaje pewne niestandardowe logowanie, ale ostatecznie jest to baza.Read(). – MGSoto

0

Dla porównania możesz po prostu przetestować, czy jest coś dostępnego, a następnie zrobić odczyt zamiast robić blokowanie odczytu, nie wiedząc, że jest jeszcze coś. Coś takiego:

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

Jeśli ktoś chce to zrobić w VB.Net, nie słuchaj tych, którzy twierdzą, że nie można tego zrobić! Może zajść konieczność zmiany parametrów ogólnych w celu dopasowania do przypadku użycia.

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 

a przykładem na to, jak go używać (mój był z Regex.Match, które czasami udaje się do nigdy nie wylądować jeśli struktura zawiera zbyt wiele dzikich kart:

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 
Powiązane problemy