По-хорошему это надо делать через async/await, однако, если интерфейс жестко привязан к значению счетчика, то есть пока он не изменился никакой работы не предусматривается, то можно опустить это
Представим, что есть такой класс контроллера в библиотеке:
class Controller
{
public static uint Counter
{
get
{
Thread.Sleep(TimeSpan.FromSeconds(1)); // Пауза имитирующая соединение с прибором итд
return (uint)(3 * (new Random()).NextDouble() + 1);
}
}
}
У него нет события, которое вызывалось бы при изменении счетчика, а нам нужно отслеживать его изменение.
Очевидно, что так как мы работаем синхронно и в одном потоке, нам важно чтобы программа не повисла. Поэтому нам нужно два параметра - время задержки перед запросом значения счетчика, чтобы прибор не грузился. И число обращений к значению счетчика.
Эти два параметра лучше вынести в сам интерфейс, чтобы можно было их менять во время работы программы. Это может пригодиться.
class MyForm
{
public void CounterWatcher()
{
Console.WriteLine("Start observing for Contoroller");
var init = Controller.Counter;
var numberOfAttempts = 5;
var pauseTimeSec = 1;
var n = 0;
Console.WriteLine($"Initial value is {init}");
while (Controller.Counter != init && n < numberOfAttempts)
{
Thread.Sleep(TimeSpan.FromSeconds(pauseTimeSec));
Console.WriteLine($"Value was not changed. Try again. Number of attempt is {n}");
n++;
}
if (numberOfAttempts == 4)
{
Console.WriteLine("Controller.Counter was not changed");
return;
}
Console.WriteLine("Controller.Counter has changed");
}
}
Вывод такой программы:
Start observing for Contoroller
Initial value is 2
Value was not changed. Try again. Number of attempt is 0
Controller.Counter has changed
Другой вариант - использовать класс Task, который запускает код в потоке параллельно с главным, но так как мы будем ждать его завершения, это все равно будет подвешивтаь главный поток.
Кроме того, внутри потока отличного от главного в WinForms нельзя обращаться к элементам форм, например, кнопкам, лейблам и так далее.
Однако это может быть удобно тем, что предоставляет встроенный и гибкий механизм отмены:
class MyForm
{
public static void CounterWatcher()
{
Console.WriteLine("Start observing for Contoroller");
var init = Controller.Counter;
var numberOfAttempts = 5;
var pauseTimeSec = 1;
Console.WriteLine($"Initial value is {init}");
var canc = new CancellationTokenSource();
canc.CancelAfter(TimeSpan.FromSeconds(pauseTimeSec*numberOfAttempts));
try
{
var watchTask = Task.Run(() =>
{
while (Controller.Counter != init)
{
Thread.Sleep(TimeSpan.FromSeconds(pauseTimeSec));
if (canc.Token.IsCancellationRequested) throw new OperationCanceledException();
Console.WriteLine($"Value has not changed.");
}
}, canc.Token);
watchTask.Wait();
Console.WriteLine("Controller.Counter has changed");
}
catch (AggregateException ae)
{
foreach (var ie in ae.InnerExceptions)
{
if (ie is OperationCanceledException)
Console.WriteLine("Controller.Counter was not changed");
else
throw; // any other exception will throw again
}
}
}
Вывод:
# В случае времени на выполнение 1 сек:
Start observing for Contoroller
Initial value is 3
Controller.Counter was not changed
# В случае времени на выполнение 5 сек:
Start observing for Contoroller
Initial value is 1
Value has not changed.
Controller.Counter has changed