Visual Basic.NET: как создать поток для обновления пользовательского интерфейса

Обычный VB-способ обработки сложной вычислительной задачи - поместить ее в фоновый рабочий поток, в то время как основной поток продолжает обрабатывать пользовательский интерфейс.

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

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

Public Class ProgressDisplay

Private trd As Thread

    Public Sub New()
        trd = New Thread(AddressOf threadtask)
        trd.Start()
    End Sub

    Private Sub threadtask()
        Dim f1 As Form1
        f1 = New Form1
        f1.Show()
        Do
            f1.Update()
            Thread.Sleep(100)
        Loop
    End Sub

End Class

Изменить: в идеале мне нужно представить клиенту такой интерфейс

Public Class ProgressDisplay
    Public Sub New()
    Public Sub Update(byval progress as int)
End Class

Клиент будет называть это так (на самом деле в неуправляемом C ++ через COM, но вы получите картину):

Dim prog = new ProgressDisplay()
DoLotsOfWork(addressof prog.update) ' DoLotsOfWork method takes a callback argument to keep client informed of progress
 J...22 июн. 2012 г., 14:02
Пожалуйста, опишите ситуацию более подробно. Трудно понять, что вы имеете в виду.
 Sideshow Bob22 июн. 2012 г., 14:19
Клиент - это код, который использует ProgressDisplay. Это также клиент методаDoLotsOfWork(progress_callback), Где я могу использовать клиент для ссылки на сам ProgressDisplay?
 Steven Doggart22 июн. 2012 г., 14:16
Похоже, вы используете слово «клиент» ссылаться как на код, использующий ProgressDisplay, так и на сам объект ProgressDisplay. Я тоже запутался.
 Sideshow Bob22 июн. 2012 г., 14:08
Смотрите обновление спасибо :)
 Sideshow Bob22 июн. 2012 г., 13:59
Проблема в том, что в моем коде не выполняется долгая работа - это происходит в клиенте для этого класса. Таким образом, я не могу контролировать, где это происходит - это в главном потоке. Что клиент должен сделать, это позвонитьSetupProgressIndicator метод в начале, иIncrementProgressIndicator метод время от времени. Можно ли заставить это работать?

Ответы на вопрос(3)

который его создал. Любой поток может сделать запрос для вызова метода в потоке пользовательского интерфейса, чтобы он мог обновить пользовательский интерфейс, используяControl.Invoke метод, но это будет просто сидеть и ждать, пока поток пользовательского интерфейса больше не занят. Если поток пользовательского интерфейса занят, пользовательский интерфейс не может быть обновлен кем-либо или кем-либо. Пользовательский интерфейс обновляется только тогда, когда основной цикл сообщений (AKAApplication.Run) обрабатывает сообщения окна в очереди и воздействует на них. Если поток пользовательского интерфейса занят, зациклен или ожидает ответа от сервера, он не сможет обработать эти оконные сообщения (если вы не вызоветеDoEvents, что я определенно не рекомендую, если это вообще возможно). Так что, да, пока пользовательский интерфейс занят, он будет заблокирован. Вот и вся причина, по которой все предлагают делать здоровенную бизнес-логику в отдельном потоке. Если это не проблема, зачем кому-то мешать создавать рабочие потоки?

Решение Вопроса

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

ВыCAN сделать это, но это, вероятно, не самое элегантное или рекомендуемое решение. Вам необходимо создать второй контекст приложения и новый цикл сообщений, который может продолжать выполняться рядом с основным потоком пользовательского интерфейса. Класс вроде этого будет работать:

Imports System.Threading

Public Class SecondUIClass

    Private appCtx As ApplicationContext
    Private formStep As Form
    Private trd As Thread
    Private pgBar As ProgressBar
    Delegate Sub dlgStepIt()

    Public Sub New()
        trd = New Thread(AddressOf NewUIThread)
        trd.SetApartmentState(ApartmentState.STA)
        trd.IsBackground = True
        trd.Start()
    End Sub

    Private Sub NewUIThread()
        formStep = New Form()
        pgBar = New ProgressBar()
        formStep.Controls.Add(pgBar)
        appCtx = New ApplicationContext(formStep)
        Application.Run(appCtx)
    End Sub

    Public Sub StepTheBar()
        formStep.Invoke(New dlgStepIt(AddressOf tStepIt))
    End Sub

    Private Sub tStepIt()
        pgBar.PerformStep()
    End Sub

End Class

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

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

У этого класса НЕТ кода очистки, никаких проверок на безопасность и т. Д. (Будьте осторожны), и я даже не уверен, что он закончится изящно - я оставляю эту задачу вам. Это просто иллюстрирует способ начать. В основной форме вы бы сделали что-то вроде:

Public Class Form1

    Private pgClass As New SecondUIClass

    Private Sub Button1_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
        Dim i As Integer
        For i = 1 To 10
            System.Threading.Thread.Sleep(1000)
            pgClass.StepTheBar()
        Next
    End Sub
End Class

Запуск приложения выше создастForm1 а также вторая форма, созданнаяpgClass, Нажатие кнопкиButton1 наForm1 будет блокировать Form1 во время прохождения цикла, но вторая форма останется живой и отзывчивой, обновляя индикатор выполнения каждый разForm1 называется.StepTheBar().

На самом деле, лучшим решением в этом случае является «клиент»; научитесь правильно программировать и в первую очередь избегайте застревать в этой головоломке. В случае, когда это абсолютно невозможно, и вы ДОЛЖНЫ создать для них компонент, который останется живым, несмотря на их плохой код, тогда вышеупомянутый подход, вероятно, является вашим единственным выходом.

тока, кроме потока диспетчеризации основных событий. Итак, вы пытаетесь сделать что-то, что не сработает.

 Sideshow Bob22 июн. 2012 г., 14:00
Хорошо. Есть ли способ уволить другогоprocess что с этим справится?

Ваш ответ на вопрос