Вопрос по applicationcontext, ui-thread, vb.net, multithreading, message-pump – Visual Basic.NET: как создать поток для обновления пользовательского интерфейса

2

Обычный 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...
Клиент - это код, который использует ProgressDisplay. Это также клиент методаDoLotsOfWork(progress_callback), Где я могу использовать клиент для ссылки на сам ProgressDisplay? Sideshow Bob
Похоже, вы используете слово «клиент» ссылаться как на код, использующий ProgressDisplay, так и на сам объект ProgressDisplay. Я тоже запутался. Steven Doggart
Смотрите обновление спасибо :) Sideshow Bob
Проблема в том, что в моем коде не выполняется долгая работа - это происходит в клиенте для этого класса. Таким образом, я не могу контролировать, где это происходит - это в главном потоке. Что клиент должен сделать, это позвонитьSetupProgressIndicator метод в начале, иIncrementProgressIndicator метод время от времени. Можно ли заставить это работать? Sideshow Bob

Ваш Ответ

3   ответа
2

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

1

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

Вы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().

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

1

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

Хорошо. Есть ли способ уволить другогоprocess что с этим справится? Sideshow Bob

Похожие вопросы