Создайте альтернативу Application.DoEvents с задержкой ожидания.

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

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), но это ПРИБЛОКИРУЕТ мое приложение во время его выполнения!
Я пытался использовать таймер, но мне ВСЁ ВСЁ нужен цикл, который не зависает, но действует как Application.DoEvents(). Это кажется невозможным.

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

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

person Andrea    schedule 09.03.2020    source источник
comment
Что не так с Таймером? Какой таймер вы пробовали? Вы можете использовать задачи? То есть иметь функцию async, которую вы можете ожидать и вызывать Task.Delay()? Вы можете передать значение задержки и Action() или Func() методу, асинхронному или не асинхронному. Кстати, если вы можете использовать асинхронный материал, Wait(5000) можно установить локально с помощью всего лишь Await Task.Delay(5000).   -  person Jimi    schedule 09.03.2020
comment
спасибо за ответ. никогда не использовал задачи или асинхронность, и я смотрел в Интернете и видел только коды языка C, поэтому не понял. Я пробовал таймер, но он не зацикливается и не ждет, пока он не завершится, пока не запустит код после того, как я вызову таймер. label1.text = DONE должен запускаться только ПОСЛЕ 5 секунд. Могу ли я создать цикл, используя задачи или асинхронную штуку, не замораживая приложение, но предоставляя ему альтернативу application.doevents, чтобы тем временем я мог делать другие вещи?   -  person Andrea    schedule 09.03.2020
comment
Какую версию Visual Studio вы используете? Вам нужно как минимум VS 2012, чтобы иметь доступ к async/await.   -  person Jimi    schedule 09.03.2020
comment
Я использую VS 2019. Просто не знаю, как использовать aync и ожидание. Так что с 2 я действительно могу создать цикл, пока таймер не будет выполнен и т. д.?   -  person Andrea    schedule 09.03.2020
comment
Суть в том, чтобы не вообще не использовать циклы. Await позволяет дождаться завершения асинхронной процедуры, прежде чем будет выполнен следующий код, в то время как текущий поток свободен для продолжения своей обработки. Вы не можете сделать это с помощью таймера, но вы можете использовать таймер для выполнения действия. Выложу пару примеров. А пока взгляните на Асинхронный программирование с помощью Async и Await.   -  person Jimi    schedule 10.03.2020


Ответы (1)


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

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

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


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

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

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

Private waitTimer As New System.Windows.Forms.Timer()
Private TimerTickHandler As EventHandler

Private Sub Wait(delay As Integer, action As Action)
    waitTimer.Interval = delay
    TimerTickHandler = New EventHandler(
        Sub()
            action.Invoke()
            waitTimer.Stop()
            RemoveHandler waitTimer.Tick, TimerTickHandler
        End Sub)
    AddHandler waitTimer.Tick, TimerTickHandler
    waitTimer.Start()
End Sub

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

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

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

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

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

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

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

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

Асинхронный метод обеспечивает удобный способ выполнения долговременной работы без блокировки потока вызывающего объекта. Вызывающий асинхронный метод может возобновить свою работу, не дожидаясь завершения асинхронного метода.

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


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

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

Асинхронизация и ожидание
Не блокировать асинхронный код


Простой метод 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 мы хотим выполнить действие (метод, который не возвращает значение) или функцию (метод, который возвращает значение), и следующий код должен выполняться только после того, как это действие или функция вернет :

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/Await для ожидания завершения выполнения задачи в ThreadPool Thread, вызывающий 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() метод:

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

person Jimi    schedule 09.03.2020
comment
Джими, во-первых, ты потрясающий. Я имею в виду подробный ваш ответ, совершенно потрясающий! Большое спасибо. Я попробовал: DelayWithTimer(5000, Sub() Me.Label1.Text = Done) и заметил, что он работает точно так же, как таймер. У него нет петли. Так что технически я могу сделать то же самое с таймером. Вот с этим и проблема. Я не хочу каждый раз переключаться на разные сабвуферы, когда мне нужно создать паузу. Если у меня есть SUB, который должен выполнять множество задач, например, просмотр веб-страниц с помощью веб-браузера, ему нужно несколько раз подождать, пока что-то не будет сделано или загружено. - person Andrea; 11.03.2020
comment
поэтому у меня ДОЛЖЕН быть какой-то цикл. Я действительно не хочу, чтобы асинхронный или таймер запускал другую подпрограмму, я просто хочу, чтобы она возобновлялась с кодом (но без использования цикла событий application.do, который использует так много ЦП), но в то же время действует как applicaiton.doevents был уволен. Извините, если я плохо объясняю. если для открытия URL-адреса в браузере используется один единственный сабвуфер, вход в систему, выполнение множества действий и все, таймер/асинхронность выше не будет работать. 1. Загрузите URL-адрес. 2. ПОДОЖДИТЕ, пока он полностью не загрузится. 3. Введите данные для входа. войти 4. ПОДОЖДИТЕ, пока не войдете.. - person Andrea; 11.03.2020
comment
с асинхронностью или таймером это означает, что я должен переходить от одного сабвуфера к другому КАЖДЫЙ РАЗ, когда мне нужно создать паузу. Имеет ли это смысл? очень не хочется этим заниматься. Нет ли способа запустить эту команду, и она не запустит команду после того, как пройдет 5 секунд? DelayWithTimer(5000, Sub() Me.Label1.Text = Готово) - person Andrea; 11.03.2020
comment
Чтобы войти на веб-страницу с помощью элемента управления WebBrowser, вы используете его событие DocumentCompleted, а не цикл. Вы не используете циклы, чтобы ждать, пока что-то произойдет, вы используете события или задачи. Например: Автоматический вход на сайт: SetAttribute не работает и Как получить значение HtmlElement внутри фреймов/IFrames?. Вам нужно научиться пользоваться этими инструментами. Опубликованный мной асинхронный метод Wait() (или await Task.Delay()) можно использовать для ожидания на месте в течение заданного времени, когда это необходимо (но не для ожидания загрузки страницы). - person Jimi; 11.03.2020
comment
Только один последний вопрос, есть идеи, как я могу решить вопрос ниже? Извините, что беспокою вас этим - person Andrea; 11.03.2020
comment
@Andrea ~ Не забудьте отметить это как ответ, если это так. SO живет и умирает своей системой вопросов и ответов. - person InteXX; 12.03.2020
comment
Извините, я немного новичок в этом стеке. Кто-нибудь видит этот комментарий? - person Andrea; 15.03.2020