Я ищу общий метод для реализации экрана ожидания во время длительных операций. Я уже несколько раз использовал многопоточную обработку, но у меня такое чувство, что я ее реализовал либо очень плохо, либо с большим трудом (а копирование / вставка - ужас!).
Я хочу, чтобы это было как можно более общим и простым, чтобы мне не пришлось реализовывать множество BackgroundWorker, обрабатывающих все виды дерьма, что усложняет обслуживание.
Вот что я хотел бы сделать - обратите внимание, что это может отличаться от того, что на самом деле возможно / наилучшей практики / чего угодно - с использованием VB.NET, Framework 2.0 (без анонимных методов):
Private Sub HandleBtnClick(sender as Object, e as EventArgs) Handles Button.Click
LoadingScreen.Show()
'Do stuff here, this takes a while!'
Dim Result as Object = DoSomethingTakingALongTime(SomeControl.SelectedObject)
LoadingScreen.Hide()
ProcessResults(Result)
End Sub
Приложение теперь полностью однопоточное, поэтому все работает в потоке графического интерфейса. Мне нужно иметь доступ к объектам в DoSomethingTakingALongTime() без получения исключений между потоками. Поток графического интерфейса ожидает завершения некоторого метода (что занимает много времени), в то время как форма LoadingScreen должна оставаться отзывчивой (она анимирована / имеет индикатор выполнения / и т. д.).
Это выполнимый / хороший подход или я считаю его слишком упрощенным? Каковы лучшие практики в этом отношении? И самое главное: как я мог реализовать такую систему? Как я уже упоминал, у меня очень мало опыта работы с потоками, так что будьте осторожны, пожалуйста :-)





В своем потоке используйте Application.Run (yourform), чтобы получить то, что вы хотите.
Обратите внимание, что вам нужно сигнализировать форме о том, чтобы как-то закрыть себя.
Я надеюсь, что вы не сочтете это бесполезным, но я бы спросил, ПОЧЕМУ вам нужен многопоточный экран ожидания? Причина использования потоковой передачи в первую очередь заключается в том, что пользовательский интерфейс остается отзывчивым, а длительные операции выполняются в фоновом режиме.
В противном случае у вас может быть просто ProgressBar в элементе управления FormLoading и DoSomethingTakingALongTime для его периодического обновления. Для этого вообще не нужны потоки.
.NET очень разборчив в том, когда и как обновляет графический интерфейс. Иногда он работает нормально, пока вы где-то не щелкнете, и он полностью перестает обновляться. Я знаю, что длинные операции должны выполняться в потоке, а не в графическом интерфейсе, но я не упоминал об этом, потому что хочу знать, возможно ли это решение.
<продолжение> Меня не особо волнует основная форма, реагировать должен только экран загрузки. Меня также не волнует, в какой логике потока выполняется, но я не могу понять это правильно, я всегда сталкиваюсь с исключениями между потоками ...
Ваша проблема в том, что вы получаете исключение между потоками, когда пытаетесь передать данные рабочего потока в поток пользовательского интерфейса. что вам нужно сделать, это проверить InvokeRequired и begininvoke перед настройкой элементов управления в пользовательском интерфейсе, чтобы вы не получили такую ошибку:
Private Sub work_CrossThreadEvent(ByVal sender As Object, ByVal e As System.EventArgs) Handles work.CrossThreadEvent
If Me.InvokeRequired Then
Me.BeginInvoke(New EventHandler(AddressOf work_CrossThreadEvent), New Object() {sender, e})
Return
End If
Me.Text = "Cross Thread"
End Sub
просто измените часть New EventHandler на обработчик событий, который вы используете.
Также я думаю, что использование фонового рабочего - неплохой метод для ваших рабочих классов, просто создайте класс для своей работы и используйте фонового работника, чтобы выполнять потоковую работу примерно так:
Public MustInherit Class Worker
Protected WithEvents worker As BackgroundWorker
Public Sub New()
worker = New BackgroundWorker()
worker.WorkerReportsProgress = True
worker.WorkerSupportsCancellation = True
End Sub
Public Sub Start()
If (Not worker.IsBusy AndAlso Not worker.CancellationPending) Then
worker.RunWorkerAsync()
End If
End Sub
Public Sub Cancel()
If (worker.IsBusy AndAlso Not worker.CancellationPending) Then
worker.CancelAsync()
End If
End Sub
Protected MustOverride Sub Work()
Private Sub OnDoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
Work()
End Sub
Public Event WorkCompelted As RunWorkerCompletedEventHandler
Private Sub OnRunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
OnRunWorkerCompleted(e)
End Sub
Protected Overridable Sub OnRunWorkerCompleted(ByVal e As RunWorkerCompletedEventArgs)
RaiseEvent WorkCompelted(Me, e)
End Sub
Public Event ProgressChanged As ProgressChangedEventHandler
Private Sub OnProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles worker.ProgressChanged
OnProgressChanged(e)
End Sub
Protected Overridable Sub OnProgressChanged(ByVal e As ProgressChangedEventArgs)
RaiseEvent ProgressChanged(Me, e)
End Sub
End Class
Public Class ActualWork
Inherits Worker
Public Event CrossThreadEvent As EventHandler
Protected Overrides Sub Work()
'do work here'
WorkABit()
worker.ReportProgress(25)
WorkABit()
worker.ReportProgress(50)
WorkABit()
worker.ReportProgress(75)
WorkABit()
worker.ReportProgress(100)
End Sub
Private Sub WorkABit()
If worker.CancellationPending Then Return
Thread.Sleep(1000)
RaiseEvent CrossThreadEvent(Me, EventArgs.Empty)
End Sub
End Class
отказ от ответственности ... немного ржавый с vb, но вы должны уловить идею.
Спасибо! Я разберусь с этим и вернусь к вам :-)
Очень и очень полезно. Отличное объяснение на простом английском языке. Спасибо!
Какая нить? Приложение является однопоточным (= поток графического интерфейса пользователя), который уже использует Application.Run для отображения основной формы.