Фоновый поток замедляется Основной поток пользовательского интерфейса визуальный базовый

Я хотел бы показать GIF внутри Picture Box в течение 2 секунд в отдельном потоке от основного потока. Я запускаю таймер, который перемещает Picture Box с изображением в основном потоке.

Чтобы проверить, я создал Picture Box и добавил то же изображение. Я запускаю фоновый поток нажатием кнопки. Очевидная ОШИБКА или проблема заключается в том, что предполагаемый фоновый поток замедляет основной поток.

Создание и реализация потоков предлагает два варианта: BackgroundWorker и Task.Run.

Я просмотрел эту статью журнала Code Magazine, в которой было предложено гораздо больше вариантов, чем я понимаю: Статья журнала Code

Также просмотрел эту статью. Не удалось преобразовать код С# в VB. ДА. Я использовал преобразователь кода: Стивен Клири

Мой код размещен ниже для фоновой темы. Нет необходимости публиковать код таймера Tick.

Вопрос, что я упустил или что я делаю неправильно ИЛИ это просто невозможно?

Private Sub myThreadMethod()
    'Await
    'Async
    Dim myThread As New Thread(AddressOf myThreadMethod)
    myThread.IsBackground = True

    myThread.Start()
    If Me.InvokeRequired = True Then
        Me.Invoke(Sub()
                      'PbT.Location = New Point(128, 132)
                      PbT.Left -= 1
                      PbT.Top += 2
                  End Sub)
        'If PbT.Bounds.IntersectsWith(btnBot.Bounds) Then
        'TextBox1.Invoke(Sub() TextBox1.Text =
    End If
    If PbT.Location.Y > 500 Then
        PbT.Invoke(Sub() PbT.Location = New Point(350, 230))
        Thread.Sleep(9000)
        myThread.Abort()
    End If

End Sub

Ответ на вопрос был добавлен Craig и ответил James_Duh


Public Class frmStart

Dim running As Boolean = False
Dim stopWatch As Stopwatch = New Stopwatch

Private Sub frmStart_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
    Cursor.Clip = New Rectangle(Me.Location, Me.Size)
    btnLPad.Left = e.X
    btnCPad.Left = e.X + 28
    btnRPad.Left = e.X + 56
End Sub

Private Sub tmrMove_Tick(sender As Object, e As EventArgs) Handles tmrMove.Tick

    Static direction As New Point(0, 4)

    Static endTime As DateTime = DateTime.Now.AddYears(1)
    If DateTime.Now > endTime Then
        PbT.Visible = False
        endTime = DateTime.Now.AddYears(1)
    End If

    If _buttons.All(Function(x) x.Button.Visible = False) Then
        pbOne.Top = 300
        PbT.Visible = False
        tbAns.Visible = True

        stopWatch.Stop()
        Dim ts = stopWatch.Elapsed
        Dim elapsedTime = $"{ts.Minutes:0} Min {ts.Seconds:00} Sec"
        tbAns.Text = elapsedTime

        running = False
        direction = New Point(0, 4)

        tmrMove.Stop()
        MsgBox("You Win")
        stopWatch.Reset()
        '================
        tbAns.Visible = False
        ResetButtons()

    End If

    If pbOne.Bounds.IntersectsWith(btnLPad.Bounds) Then
        direction = New Point(-2, -3)
    End If
    If pbOne.Bounds.IntersectsWith(btnRight.Bounds) Then
        Static spC As Integer = 1
        spC += 1
        direction = If(spC Mod 2 = 0, New Point(-3, 2), New Point(-5, 1))
    End If

    If pbOne.Bounds.IntersectsWith(btnLeft.Bounds) Then
        direction = New Point(4, 2)
    End If

    If pbOne.Bounds.IntersectsWith(btnCPad.Bounds) Then
        direction = New Point(direction.X, -4)
    End If

    If pbOne.Bounds.IntersectsWith(btnRPad.Bounds) Then
        Static spA As Integer = 1
        spA += 1
        direction = If(spA Mod 2 = 0, New Point(1, -5), New Point(-3, -4))
    End If

    If pbOne.Bounds.IntersectsWith(btnTop.Bounds) Then
        Static spE As Integer = 1
        spE += 1
        direction = If(spE Mod 2 = 0, New Point(-3, 2), New Point(4, 2))
    End If

    If pbOne.Bounds.IntersectsWith(btnBot.Bounds) Then
        tmrMove.Stop()
        running = False
        pbOne.Top = 200
        PbT.Visible = False
        MsgBox("Press S to Start")
    End If

    pbOne.Left += direction.X
    pbOne.Top += direction.Y

    For Each x In _buttons
        If pbOne.Bounds.IntersectsWith(x.Button.Bounds) Then
            endTime = DateTime.Now.AddSeconds(2.0)
            x.Button.Visible = False
            x.Button.Location = New Point(350, -30)
            PbT.Location = New Point(x.Location.X + 20, 31)
            PbT.Visible = True
            direction = New Point(3, 3)
        End If
    Next
End Sub
Private Sub frmStart_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown

    If running AndAlso e.KeyCode = Keys.P Then
        tmrMove.Stop()
    End If

    If e.KeyCode = Keys.S Then

        If Not running Then
            stopWatch.Start()
            running = True
        End If

        tmrMove.Interval = 1
        tmrMove.Start()

    End If

End Sub
Public Sub frmStart_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
    'Form Property KeyPreview needs to be set to True
    '=================================================

    If Asc(e.KeyChar) = 27 Then

        Const message As String = "YES" & "   Exit Program" + vbCrLf + vbNewLine + "NO" & "     Read Directions"
        Const caption As String = "Exit OR Return"

        Dim result = MessageBox.Show(message, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question)

        If result = DialogResult.Yes Then
            Application.Exit()
        ElseIf result = DialogResult.No Then
            frmInfo.Show()
            Close()
        End If
    End If

End Sub

Private _buttons As (Button As Button, Location As Point)() = Nothing

Private Sub frmStart_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    If _buttons Is Nothing Then
        _buttons =
        {
            (btnB1, New Point(29, 32)),
            (btnB2, New Point(110, 32)),
            (btnB3, New Point(191, 32)),
            (btnB4, New Point(272, 32)),
            (btnB5, New Point(353, 32)),
            (btnB6, New Point(434, 32)),
            (btnB7, New Point(515, 32)),
            (btnB8, New Point(596, 32)),
            (btnB9, New Point(677, 32))
        }
    End If
    ResetButtons()
End Sub

Private Sub ResetButtons()
    For Each x In _buttons
        x.Button.Visible = True
        x.Button.Location = x.Location
    Next
End Sub

Конец класса

Приведенный выше код был получен от Enigmativity и ИСПРАВЛЯЕТ ряд проблем. См. его комментарии о секундомере и воспроизведении гифки. Кроме того, с его кодом игра работает на 70 % быстрее


person Vector    schedule 14.09.2020    source источник
comment
Вы не можете создать, получить доступ или обновить любой элемент пользовательского интерфейса в потоке, который не является потоком пользовательского интерфейса. Период.   -  person Enigmativity    schedule 15.09.2020
comment
Также звонить Thread.Abort() опасно. Его следует вызывать только тогда, когда вы пытаетесь принудительно закрыть приложение. Это может оставить среду выполнения в неопределенном состоянии и повлиять на все оставшиеся потоки и код, который вы выполняете. Вы должны позволить потокам заканчиваться естественным образом.   -  person Enigmativity    schedule 15.09.2020
comment
Почему вы хотите использовать другой поток для этого? Что требует большой обработки, которую вы хотите убрать из потока пользовательского интерфейса?   -  person Enigmativity    schedule 15.09.2020
comment
Переместите все из Dim myThread ... в myThread.Start() вне метода и запустите поток откуда-нибудь. Поместите остальную часть кода в цикл While true и добавьте, например, Thread.Sleep(50). Удалите myThread.Abort() и просто Return. Вы должны проверить, удален ли ваш элемент управления, потому что, если вы тем временем закроете форму, произойдет крах.   -  person Jimi    schedule 15.09.2020
comment
Это можно просто сделать с помощью таймера или запуска задачи. В этом случае вы ожидаете Task.Delay() вместо Thread.Sleep() и BeginInvoke() вместо InvokeRequired/Invoke(). Передайте CancellationToken (из CancellationTokenSource), если вы хотите Cancel() выполнить задание до его завершения. Все 3 метода теперь асинхронны (ни один из них не будет блокировать поток пользовательского интерфейса. Даже стандартный таймер, поскольку вы просто перемещаете элемент управления).   -  person Jimi    schedule 15.09.2020
comment
@Enigmativity Спасибо за прерывание темы. Я попытался ОСТАНОВИТЬ фоновую тему, что было последней попыткой. Я попытался поместить гифку в Picture Box и воспроизвести ее в течение 2 секунд, когда btnBALL пересекается с btnBRICK, как в игре Breakout.   -  person Vector    schedule 15.09.2020
comment
@Jimi Хорошо, пусть я понимаю, могу ли я отложить основной поток, который использует большую часть вычислительной мощности, и просто показать фоновый поток на 2 секунды. Но как мне остановить фоновый поток от продолжения RUN, у него есть gif, который кажется никогда не останавливайся   -  person Vector    schedule 15.09.2020
comment
Может быть, я что-то упускаю здесь, но разве здесь не происходит немного непреднамеренной рекурсии? Создание потока с делегатом внутри метода делегата? это кажется мне немного неправильным   -  person Hursey    schedule 15.09.2020
comment
Вы не задерживаете основной поток. Если вы имеете в виду задачу, вы используете await Task.Delay(milliseconds) для создания задачи, которая продолжается после указанного времени. Используя свой поток (измененный, как описано), просто Return после Thread.Sleep(9000), чтобы закончить поток. Я не знаю, о чем идет речь в GIF, так как у вас здесь нет GIF. Если вам нужна помощь по этому поводу, вам придется объяснить, чего вы пытаетесь достичь, намного лучше. Если ваш PictureBox (я так думаю) содержит анимированный GIF, вам нечего делать. ImageAnimator в любом случае будет анимировать GIF.   -  person Jimi    schedule 15.09.2020
comment
@Vector - Что вы имеете в виду под гифкой, которая, кажется, никогда не останавливается?   -  person Enigmativity    schedule 15.09.2020
comment
В основной теме @Jimi есть окно с изображением, которое перемещается по таймеру, если оно пересекается с кнопкой, тогда кнопка перемещается из формы в этот момент. Я хочу, чтобы окно с изображением, в котором есть gif, перемещалось в старое местоположение кнопки и воспроизводилось. gif в течение 2 секунд все время это происходит окно изображения с тем, что таймер движется, продолжает двигаться   -  person Vector    schedule 15.09.2020
comment
@Enigmativity Пока окно с изображением с изображением видно, оно воспроизводится, возможно, потому, что оно находится внутри метода таймера, просто окно с изображением с изображением, просто находящееся на форме, воспроизводится в бесконечном цикле.   -  person Vector    schedule 15.09.2020
comment
Позвольте мне попробовать написать метод, который вызывается, когда происходит IntersectsWith, который перемещает окно изображения в старое расположение кнопки, которая представляет собой кирпичик, такой как игра Breakout, и делает его видимым в течение 2 секунд. ВСЕ ЕЩЕ НЕ уверен, что это работоспособная идея, которую я сделал эта же игра на JavaFX, и для работы с потоками требуется много процессов.   -  person Vector    schedule 15.09.2020
comment
НЕТ, вы не можете управлять gif в основном потоке, если вы не установите для Enable значение False, он запускается и пытается установить Picture Box обратно для Enabled в True с кодом без изменений. Нужна новая концепция дизайна. Спасибо за комментарии.   -  person Vector    schedule 15.09.2020
comment
@Vector - я изо всех сил пытаюсь понять, что вы говорите. Это не одно предложение - НЕТ, вы не можете управлять gif в основном потоке, если вы не установите Enable в False, он запускается и пытается установить Picture Box обратно в Enabled в True с кодом без изменений Нужна новая концепция дизайна Спасибо за Комментарии. Не могли бы вы попытаться обдумать то, что вы говорите, и дать нам четко понять, каковы ваши цели? Мы были бы очень рады помочь вам, если бы знали, что вам нужно сделать.   -  person Enigmativity    schedule 15.09.2020
comment
@Vector - Пожалуйста, не вносите изменения в вопрос, как вы. Всегда добавляйте в конец, если вам нужно добавить новую информацию. Подобные правки, которые делают недействительными существующие ответы, затрудняют понимание вопросов.   -  person Enigmativity    schedule 23.09.2020


Ответы (1)


Пытаясь воспроизвести это, НЕ видя вашего кода тика таймера, я написал свой собственный
Понимание ИГРОВОГО дизайна Breakout поможет всем, кто пытается следовать шагам Vectors, нужно показать GIF в течение X количества секунд
Сначала вам нужно для секундомера, встроенного в таймер
Во-вторых, вам нужно знать, когда установить время ОКОНЧАНИЯ в секундах. Логика потребует, чтобы это произошло, когда МЯЧ пересекается с КИРПИЧОМ
Итак, мы пишем функцию под названием Fire, см. код ниже
НЕТ необходимости иметь GIF для каждого КИРПИЧА, поэтому теперь нам нужно переместить наш единственный GIF в правильный КИРПИЧ и позволить ему работать в течение X секунд. Нам также нужно сделать GIF видимым ПОЧЕМУ вы можете спросить, включены ли они и видны ли они run for ever Было проще просто управлять видимостью
Вам также нужен код внутри метода Timer Tick, чтобы сделать gif невидимым через X секунд
Извините за отсутствие декларативных переменных
pbOne = BALL & btnB1 = BRICK & PbT = Picture Box с gif

 Public Function Fire() As Integer
    'Set Stopwatch 5 sec in the future
    nT = CDbl(DateTime.Now.AddSeconds(5).ToString("ss"))
    Return CInt(nT)
End Function


Private Sub tmrMove_Tick(sender As Object, e As EventArgs) Handles tmrMove.Tick

    'nS is Rolling Time
    nS = CDbl(DateTime.Now.ToString("ss"))
    If nS > nT Then
        PbT.Visible = False
    End If

    If pbOne.Bounds.IntersectsWith(btnB1.Bounds) Then
        btnB1.BackColor = Color.Red
        btnB1.Visible = False
        Fire()
        Y = btnB1.Location.Y
        X = btnB1.Location.X
        PbT.Location = New Point(X + 20, Y)
        PbT.Visible = True
        btnB1.Location = New Point(350, -30)
        rndNUM = 8
    End If
person James_Duh    schedule 15.09.2020
comment
Это ужасно. Это не работает. Но это можно исправить. Что, если текущее время 2020/09/16/ 07:57:54? Тогда таймер установит nT в "59", а таймер никогда не установит PbT в скрытое. - person Enigmativity; 16.09.2020
comment
Также кажется, что вычисления для местоположения PbT выполняются неправильно. - person Enigmativity; 16.09.2020
comment
@Enigmativity Заключу сделку, я положу готовый EXE-файл проекта и CODE в свой GitHub, чтобы вы могли увидеть, как он работает. Я изменил несколько вещей как в функции Fire, так и в tmrMove. Я жестко закодировал данные о местоположении для PbT, но честно легкие игры Если бы я был разработчиком игр, я бы не использовал VB, я бы больше не работал в Unity - person Vector; 20.09.2020
comment
@Vector - хотелось бы увидеть окончательный код. - person Enigmativity; 21.09.2020
comment
@Enigmativity Я создал zip-файл проекта и включил EXE-файл Inno Setup, который находится по адресу github.com/e-d-n/ Прорыв Наслаждайтесь - person Vector; 21.09.2020
comment
@Vector - я проверил код. Если вы нажмете на один из кирпичей в верхней части экрана, когда ваши часы показывают 58 или 59 секунд, ваш PbT останется видимым до тех пор, пока не будет нажат другой кирпич. Он не исчезает через две секунды. - person Enigmativity; 22.09.2020
comment
@Vector — взгляните на этот код: dotnetfiddle.net/eFqbKA . Я провел рефакторинг. Он должен работать так же, как ваш исходный код, но без ошибки 58 и 59 секунд. Вы не можете запустить его по ссылке — просто скопируйте и вставьте этот источник в свой код. - person Enigmativity; 22.09.2020
comment
@Enigmativity Спасибо за код. Использование New Point действительно ускоряет игру. Мне нужно продолжать смотреть на часть кода секундомера. Я пропускаю улучшение. Итак, нам ясно, что ваш код показывает GIF только в течение 2 секунд, мой время от времени спотыкается. Это значительное улучшение, из-за которого мы все попали в ловушку. ЕСЛИ ЭТО РАБОТАЕТ, оно должно быть правильным. Я пытался проверить проблему со временем, изменив часы компьютера на 58, но я искал другую проблему. ДА, теперь я понял. Спасибо. - person Vector; 23.09.2020