У меня есть функция, которая вызывает запрос на чтение или запись на последовательном порту и затем возвращает значение, которое было прочитано. Я использую Commstudio express (я реализую класс из Commstudio), но функции тайм-аута не работают вообще, поэтому я пытаюсь реализовать свой собственный тайм-аут. В настоящее время у меня есть таймер, который устанавливается по запросу для чтения или записи на порт, и если таймер отключается, обратный вызов закрывает соединение, вызывающее исключение. Я попытался вызвать обратный вызов таймера для исключения, но исключение должно быть распространено через поток, который вызывал исходную функцию чтения/записи, поэтому таким образом он работает, но я чувствую, что это беспорядочно, и там должен быть лучшим способом сделать то, что я хочу.
Реализация тайм-аута функции, возвращающей значение
Ответ 1
Вот общее решение, которое позволяет вам обернуть любой метод в таймаут:
http://kossovsky.net/index.php/2009/07/csharp-how-to-limit-method-execution-time/
Он использует полезную noreferrer → Thread.Join, которая принимает тайм-аут в миллисекундах, а не вручную, используя таймеры. Единственное, что я сделал бы по-другому, - это обмен значком успеха и значением результата в соответствии с шаблоном TryParse следующим образом:
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;
}
И вот как вы его используете:
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");
Вы также можете добавить перегрузки, которые принимают Action вместо Func, если вы хотите выполнить метод, который не возвращает значение.
Ответ 2
Похоже, что вы делаете блокировку чтения/записи. Что вы хотите сделать, это неблокирующее чтение/запись.
Вероятно, есть способ сообщить COM-порту, что вы хотите не блокировать.
Вы уверены, что таймауты не работают с commstudio? возможно, вам нужно сделать что-то особенное, чтобы их инициализировать.
В любом случае вы хотите прочитать как можно больше данных, и если ни один из них не доступен тайм-аут (в зависимости от того, каково значение тайм-аута). Вы хотите продолжать цикл, пока нет доступных данных, и нет ошибки, а затем возвратите условие ожидания, если не было ничего доступного.
Сделайте функцию чтения функцией integer. отрицательные значения = значение ошибки, например. -1 = время ожидания, положительное количество прочитанных байтов... по крайней мере, так, как я это делал.
Ответ 3
Для comport вы можете просто проверить, есть ли что-нибудь доступное, а затем сделать чтение, а не читать блокировку, не зная, что что-то еще есть. Что-то вроде:
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--;
}
Ответ 4
Если кто-то хочет сделать это в VB.Net, не слушайте тех, кто говорит, что это невозможно! Возможно, вам придется изменить свои общие параметры в соответствии с вашим вариантом использования.
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
И пример того, как его использовать (мой был с Regex.Match
, который иногда уходит в никогда не приземляться, если шаблоны содержат слишком много диких карт:
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