Создать альтернативу задержки ожидания для Application.DoEvents - PullRequest
0 голосов
/ 09 марта 2020

В течение многих лет я создаю задержки в своем программном обеспечении, используя, например:

Wait(10000)

Sub Wait(milliseconds)
    <here I get the current time>
    Do
         <here I loop until the current time passed in seconds and then exit this loop>
         Application.DoEvents()
    Loop
End Sub

Проблема в том, что это использует много ЦП. Я попытался Thread.Sleep(1000), но это ЗАМОРАЖИВАЕТ мое приложение во время его работы!
Я пытался использовать таймер, но Я ВСЕ ЕЩЕ нуждаюсь в oop, который еще не зависает, действует как Application.DoEvents (). Это кажется невозможным.

Моя цель сделать это:

label1.text = "ok about to start"
Wait(5000)        
' the following line CAN NOT run until after 5 seconds. 
label1.text = "DONE"

1 Ответ

2 голосов
/ 10 марта 2020

Как выполнить код после задержки.

Существуют различные методы для выполнения кода после задержки или асинхронно, после задержки или без нее. Здесь я рассматриваю Timer и простые реализации шаблона Async / Await.

В любом случае следует избегать l oop, который вызывает Application.DoEvent().


► Использование таймера для задержки выполнение одной инструкции или кода в одном или нескольких методах:

Вы не можете ожидать таймер, но вы можете использовать метод, который создает таймер и выполняет действие, когда таймер вызывает его событие, сигнализируя указанный интервал истек.

В следующем методе в качестве аргументов принимают значение delay и действие.
Задержка используется для установки интервала таймеров, а действие представляет собой код, который будет выполняться, когда отметки времени таймера ( Я использую System. Windows .Forms.Timer здесь, так как вы, похоже, ссылаетесь на приложение WinForms).

Private Sub DelayWithTimer(delay As Integer, action As Action)
    Dim localTimer = New System.Windows.Forms.Timer()
    localTimer.Interval = delay
    AddHandler localTimer.Tick,
        Sub()
            action.Invoke()
            localTimer.Enabled = False
            RemoveHandler localTimer.Tick, Nothing
            localTimer.Dispose()
        End Sub
    localTimer.Enabled = True
End Sub

Мы можем вызывать этот метод в любое время, когда нам нужно выполнить код после задержки.

Мы можем задать Действие простой инструкцией:
в этом случае текст label1 будет установлен на «Готово» через 5 секунд, в то время как поток пользовательского интерфейса продолжит свои операции:

label1.text = "About to Start..."
DelayWithTimer(5000, Sub() Me.label1.Text = "Done")

Действие также может быть методом:

Private Sub SetControlText(control As Control, controlText As String)
    control.Text = controlText
End Sub

' Elsewhere
DelayWithTimer(5000, Sub() SetControlText(Me.label1, "Done"))

Конечно, метод SetControlText() может выполнять более сложный код и, при необходимости, устанавливать сам таймер, вызывая DelayWithTimer().

► Использование шаблона Async / Await .

Метод asyn c предоставляет удобный способ для потенциально длительной работы без блокировки потока вызывающего. Вызывающий метод asyn c может возобновить свою работу, не дожидаясь, пока метод asyn c завершит работу sh.

Проще говоря, добавление Asyn c модификатора к методу позволяет использовать оператор Await для ожидания завершения асинхронной процедуры перед выполнением следующего кода. , в то время как текущий поток может продолжить обработку.


▬ Обратите внимание, что модификатор Async всегда применяется к Function(), который возвращает Task или Task(Of something) ( что-то может быть любым значением / ссылкой, которое может вернуть метод).
Это применяется только к Sub(), только когда метод Sub (void) представляет Event Handler. Это очень важно запомнить и применять без исключений (если вы не совсем понимаете последствия). 1080

Прочтите документы об этом (в предыдущей ссылке), а также:

Asyn c и Await
Не блокировать на Asyn c Код


Простой метод Async может быть использован для задержки выполнения действия:

Private Async Function Wait(delay As Integer, action As Action) As Task
    Await Task.Delay(delay)
    action?.Invoke()
End Function

Это похоже на функции таймера и действует аналогичным образом. Разница в том, что вы можете Await этот метод и выполнять код, который он запускает асинхронно, и ждать его завершения, чтобы запустить другой код после возврата метода. Вызывающий поток (здесь поток пользовательского интерфейса) продолжит свои операции, пока ожидается метод Wait().

Предположим, что в обработчике Button.Click мы хотим выполнить Action (метод, который не возвращает значение) или функцию (метод, который возвращает значение), а следующий код должен выполнить только после того, как это действие или функция вернет:

Private Async Sub button1_Click(sender As Object, e As EventArgs) Handles button1.Click
    Await Wait(5000, New Action(Sub() Me.label1.Text = "Done"))
    Await Wait(5000, Nothing)
    ' (...)
    ' ... More code here. It will execute after the last Await completes
End Sub

Здесь мы инструктируем подождать 5 секунд, затем установить текст метки в значение, подождать другие 5 секунд, ничего не делая, затем выполнить код, который следует за

Если нам не нужно выполнять действие, мы можем просто использовать Task.Delay () :

Private Async Sub button1_Click(sender As Object, e As EventArgs) Handles button1.Click
    label1.text = "About to Start..."
    Await Task.Delay(5000)
    label1.Text = "Done"
    ' (...)
    ' ... More code here. It will execute after the last Await completes
End Sub

Мы также можем использовать Async / Ожидание завершения выполнения задачи в потоке ThreadPool , вызов Task.Run () для выполнения кода в лямбда-выражении:
(просто пример, мы не должны использовать ThreadPool Thread для такой простой задачи)

Private Async Sub button1_Click(sender As Object, e As EventArgs) Handles button1.Click
    label1.text = "About to Start..."
    Await Task.Run(Async Function()
                       Await Task.Delay(5000)
                       BeginInvoke(New Action(Sub() Me.label1.Text = "Done"))
                   End Function)
    ' (...)
    ' ... More code here. It will execute after the last Await completes
End Sub

См. также Task.ContinueWith () метод:

Создает продолжение, которое выполняется асинхронно после завершения целевой задачи.

...