2

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

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

Вот что у меня есть. Единственная проблема заключается в том, что в то время как окно пользовательского интерфейса (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 

Edit: В идеале мне нужно представить интерфейс, как это клиент

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 
+0

Проблема заключается в том, что в моем коде длинная работа не выполняется - это происходит в клиенте для этого класса. Таким образом, я не могу контролировать, где это происходит - это в основном потоке. То, что нужно сделать клиенту, это вызвать метод «SetupProgressIndicator» в начале и метод «IncrementProgressIndicator» время от времени. Можно ли заставить его работать? –

+0

Пожалуйста, опишите ситуацию более подробно. Трудно понять, что вы имеете в виду. –

+0

См. Update thanks :) –

ответ

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

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

+0

Хорошо. Есть ли способ убрать другой * процесс *, который может справиться с этим? –

2

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

 Смежные вопросы

  • Нет связанных вопросов^_^