Многопоточный UI

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Многопоточный UI

Сообщение Mleha » 21.02.2011 (Пн) 9:49

Доброго времени суток!

Появилась проблема:
Нужно сделать приложение с многопоточным UI.
В идеале на главную форму должен добавляться UserControl, UI которого будет выполнятся в отдельном потоке.
Может произойти так, что UI этого UserControl "повиснет" (из-за присутствия на нем WebBrowser)
Тогда остальная часть приложения должна продолжать работать.

Сейчас сделал:
Создание экземпляра класса в новом потоке:
Код: Выделить всё
Dim NUI As New UIThreading
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim thread As New Threading.Thread(AddressOf NUI.createConrol)
    thread.SetApartmentState(ApartmentState.STA)
    AddHandler NUI.CntReady, AddressOf InvokeCntReady
    thread.Start()
  End Sub

При запуске потока в режиме STAThread падает эксепшен во время добавления UserControl на основную форму:
Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "DoLooper" не из того потока, в котором он был создан.

Режим MTAThread не подходит, из-за наличия в UserControl WebBrowser (эксепшен при создании UserControl):
Создание экземпляра элемента управления ActiveX "8856f961-340a-11d0-a96b-00c04fd705a2" невозможно: текущий поток не находится в однопоточном контейнере.

Прием ссылки на созданный UI объект:
Код: Выделить всё
  Private Sub cntReady(ByVal wb As Control)
    TabControl1.TabPages(0).Controls.Add(wb)
  End Sub
  Private Delegate Sub DelegatewebReady(ByVal wb As Control)
  Public Sub InvokeCntReady(ByVal wb As Control)
    If Me.InvokeRequired Then
      Dim args As Object() = {wb}
      Dim Delegate_cntReady As DelegatewebReady = AddressOf InvokeCntReady
      Me.Invoke(Delegate_cntReady, args)
    Else
      cntReady(wb)
    End If
  End Sub

Класс для создания экземпляра UserControl:
Код: Выделить всё
Public Class UIThreading
  Property dl As Control
  Event CntReady(ByVal wb As Control)
  Sub createConrol()
    dl = New DoLooper
    RaiseEvent CntReady(dl)
    dolOop()
  End Sub
  Sub dolOop()
    CType(dl, DoLooper).InvokeStartDoLoop()
  End Sub
End Class

UserControl (на нем расположен Button и WebBrowser):
Код: Выделить всё
Public Class DoLooper
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Do : Loop
  End Sub
  Private Sub StartDoLoop()
    Do : Loop
  End Sub
  Private Delegate Sub DelegateStartDoLoop()
  Public Sub InvokeStartDoLoop()
    If Me.InvokeRequired Then
      Dim args As Object() = {}
      Dim Delegate_StartDoLoop As DelegateStartDoLoop = AddressOf InvokeStartDoLoop
      Me.Invoke(Delegate_StartDoLoop, args)
    Else
      StartDoLoop()
    End If
  End Sub
End Class


Вопрос: как правильно создать в отдельном потоке экземпляр UserControl, затем поместить его на основную форму чтобы он оставался работать в отдельном потоке и его зависание не мешало работе остальной программе?

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Многопоточный UI

Сообщение FireFenix » 21.02.2011 (Пн) 12:36

Mleha писал(а):Вопрос: как правильно создать в отдельном потоке экземпляр UserControl, затем поместить его на основную форму чтобы он оставался работать в отдельном потоке и его зависание не мешало работе остальной программе?

Т.к. WebBrowser - контрол, имеющий родителя, то он должен быть запущен в потоке родителя. Т.е. в коде ты инвокаешь методы WebBrowser, но родитель пытается работать на прямую, поэтому и ошибка

1) DoEvents
2) Отдельная форма как контрол с WebBrowser'ом, т.е. не делать отдельный UserControl, а делать на отдельной форме
3) Прослойка, которая обрабатывает все сообщения между окном и WebBrowser
4) Обработка запросов в одном потоке, отображение результата в WebBrowser в главном потоке
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Re: Многопоточный UI

Сообщение Mleha » 21.02.2011 (Пн) 18:06

Сделал вторую форму в отдельном потоке. Все работает.
Вызовы инвокаются в обе стороны.
Осталась проблема: не могу уничтожить повисшую форму. При попытке инвокер не срабатывает и в результате повисает основной поток (который вызвал инвокера)

Не понятно зачем использовать DoEvents если нужна именно многопоточность, если форма повиснет из-за WebBrowser, то DoEvents некому вызывать будет))

4-й пункт не понял((
FireFenix писал(а):4) Обработка запросов в одном потоке, отображение результата в WebBrowser в главном потоке


Если отображать "результат" WebBrowser в главном потоке, значит он должен быть создан в нем, а от этого и есть цель уйти.
Или снова та же проблема, которую и решаем: как создать элемент UI в отдельном потоке и поместить его внутрь другого потока UI с сохранением поточности.

И все же в идеале все должно работать внутри одной формы, хотя бы визуально. Пытался затолкать вторую форму внутрь первой с помощью API setparent:
Код: Выделить всё
  <DllImport("User32", CharSet:=CharSet.Auto, ExactSpelling:=True)> _
  Public Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndParent As IntPtr) As IntPtr
  End Function
....

  Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
    SetParent(frm2.Handle, Panel1.Handle)
  End Sub

снова ругается, что я не прав(((. Попытки пропустить добавление Form2 на Form1 через инвокер ни к чему не привели((
Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "Form2" не из того потока, в котором он был создан.
Вложения
multiThreadUITest.zip
Многопоточный UI
(179.34 Кб) Скачиваний: 183

1Steps
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 505
Зарегистрирован: 20.12.2006 (Ср) 0:50
Откуда: New York

Re: Многопоточный UI

Сообщение 1Steps » 21.02.2011 (Пн) 23:40

Код: Выделить всё
Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim t As New Threading.Thread(AddressOf InvokeForm)
        t.Start()
    End Sub

    Private Sub InvokeForm()
        Me.Invoke(New MethodInvoker(AddressOf AddControls))
    End Sub

    Private Sub AddControls()
        Dim p As New Point(10, 10)
        For i As Integer = 0 To 2
            Dim b As New Button
            b.Text = "From Another Thread " & i
            b.Size = New Size(150, 23)
            b.Location = p
            p.Y += 30
            AddHandler b.Click, AddressOf ButtonFromAnotherThread_Click
            Me.Controls.Add(b)
        Next
    End Sub

    Private Sub ButtonFromAnotherThread_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        MsgBox(DirectCast(sender, Button).Text)
    End Sub
End Class
Удалена за ненадобностью.

Dmitriy2003
Постоялец
Постоялец
 
Сообщения: 690
Зарегистрирован: 27.05.2003 (Вт) 22:47
Откуда: Deutschland

Re: Многопоточный UI

Сообщение Dmitriy2003 » 22.02.2011 (Вт) 0:12

Mleha писал(а):Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "DoLooper" не из того потока, в котором он был создан.

Control.CheckForIllegalCrossThreadCalls

true if calls on the wrong thread are caught; otherwise, false.

When a thread other than the creating thread of a control tries to access one of that control's methods or properties, it often leads to unpredictable results. A common invalid thread activity is a call on the wrong thread that accesses the control's Handle property. Set CheckForIllegalCrossThreadCalls to true to find and diagnose this thread activity more easily while debugging.

Single-Threaded Apartments, Threading Model, Safe, Simple Multithreading in Windows Forms, Multithreaded Windows Forms Control Sample, Лекция: Асинхронное программирование

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Re: Многопоточный UI

Сообщение Mleha » 22.02.2011 (Вт) 6:09

1Steps писал(а):
Код: Выделить всё
Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim t As New Threading.Thread(AddressOf InvokeForm)
        t.Start()
    End Sub

    Private Sub InvokeForm()
        Me.Invoke(New MethodInvoker(AddressOf AddControls))
    End Sub

    Private Sub AddControls()
        Dim p As New Point(10, 10)
        For i As Integer = 0 To 2
            Dim b As New Button
            b.Text = "From Another Thread " & i
            b.Size = New Size(150, 23)
            b.Location = p
            p.Y += 30
            AddHandler b.Click, AddressOf ButtonFromAnotherThread_Click
            Me.Controls.Add(b)
        Next
    End Sub

    Private Sub ButtonFromAnotherThread_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        MsgBox(DirectCast(sender, Button).Text)
    End Sub
End Class

Не понял смысла данного кода: мы создаем поток выполняем метод в нем. В методе вызов инвокается в основной поток и кнопки создаются уже внутри основного потока.

Dmitriy2003
Постоялец
Постоялец
 
Сообщения: 690
Зарегистрирован: 27.05.2003 (Вт) 22:47
Откуда: Deutschland

Re: Многопоточный UI

Сообщение Dmitriy2003 » 22.02.2011 (Вт) 14:54

Идиотизм выносить GUI, вместо логики, в отдельный поток. Да еще и пользовательский элемент управления - это бред.
Вложения
bad.mth.logic.rar
С:\Windows\Microsoft.NET\Framework\v2.0.50727\csc /target:winexe /optimize+ /out:bad.exe *.cs
(1.41 Кб) Скачиваний: 188

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Re: Многопоточный UI

Сообщение Mleha » 22.02.2011 (Вт) 19:26

Dmitriy2003 писал(а):Идиотизм выносить GUI, вместо логики, в отдельный поток. Да еще и пользовательский элемент управления - это бред.

Оно нужно чтобы повисшая часть UI не застопорила работу остального UI. Повешать может WebBrowser. В простом однопоточном UI достаточно в TabControl затолкать несколько страниц, в каждой WebBrowser и заставить каждый браузер открыть что-нибудь тяжелое. После по-переключаться во время загрузки веб-страниц между табами...тогда весь UI повисает. Сейчас от выноса пользовательского элемента управления отказался. Выношу форму..но разделение интерфейса на несколько окон не красиво. Как объединить все внутри одной формы с сохранением отдельных потоков UI не знаю. Хотя если удастся объединить все внутри одной формы, то контейнером для WebBrowser может быть как форма, так и UserControl или любой контейнер, поддерживающий инвок-методы.

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Многопоточный UI

Сообщение FireFenix » 22.02.2011 (Вт) 19:32

Mleha писал(а):
FireFenix писал(а):4) Обработка запросов в одном потоке, отображение результата в WebBrowser в главном потоке


Если отображать "результат" WebBrowser в главном потоке, значит он должен быть создан в нем, а от этого и есть цель уйти.
Или снова та же проблема, которую и решаем: как создать элемент UI в отдельном потоке и поместить его внутрь другого потока UI с сохранением поточности.

Ничего не мешает создавать весь GUI в главном потоке, а обрабатывать результаты в другом. Если всё в динамике, то в основном потоке ещё делается функа распределения данных

Mleha писал(а):Осталась проблема: не могу уничтожить повисшую форму. При попытке инвокер не срабатывает и в результате повисает основной поток (который вызвал инвокера)

ммм... раз в отдельном потоке, то наверное сразу Thread.Abort() ? :)
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Re: Многопоточный UI

Сообщение Mleha » 22.02.2011 (Вт) 20:04

FireFenix писал(а):
Mleha писал(а):
FireFenix писал(а):4) Обработка запросов в одном потоке, отображение результата в WebBrowser в главном потоке


Если отображать "результат" WebBrowser в главном потоке, значит он должен быть создан в нем, а от этого и есть цель уйти.
Или снова та же проблема, которую и решаем: как создать элемент UI в отдельном потоке и поместить его внутрь другого потока UI с сохранением поточности.

Ничего не мешает создавать весь GUI в главном потоке, а обрабатывать результаты в другом. Если всё в динамике, то в основном потоке ещё делается функа распределения данных

Mleha писал(а):Осталась проблема: не могу уничтожить повисшую форму. При попытке инвокер не срабатывает и в результате повисает основной поток (который вызвал инвокера)

ммм... раз в отдельном потоке, то наверное сразу Thread.Abort() ? :)


Если GUI - кнопочки и другие элементы, которые запускают пользовательский код, тогда конечно можно без проблем запустить его в отдельном потоке.
но создать WebBrowser в основном потоке, и заставить его отрисовывать страницу в другом - это не возможно. Если не так, то прошу показать пример))
Проблема в потенциальном повисании именного GUI WebBrowser'a, и как следствие повисание всего потока, в котором был создан WebBrowser.

Thread.Abort() как средство прибивания повисшего потока работает, форма закрывается. Тут добавлю слой для ожидания ответа потока, и если нет ответа в течении некоторого времени, то поток будет прибиваться.
Тогда остается вопрос объединения разнопоточного GUI внутри одного контейнера

Dmitriy2003
Постоялец
Постоялец
 
Сообщения: 690
Зарегистрирован: 27.05.2003 (Вт) 22:47
Откуда: Deutschland

Re: Многопоточный UI

Сообщение Dmitriy2003 » 23.02.2011 (Ср) 0:17

Mleha писал(а):но создать WebBrowser в основном потоке, и заставить его отрисовывать страницу в другом - это не возможно.

Mleha писал(а):Тогда остается вопрос объединения разнопоточного GUI внутри одного контейнера

Зачем вообще это делать. :roll:
И специально для вас продолжение извращений с много поточной логикой, :evil:
Вложения
wfa1.rar
WebBroswer, SetParent, Thread, GUI
(15.3 Кб) Скачиваний: 180

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Re: Многопоточный UI

Сообщение Mleha » 23.02.2011 (Ср) 10:54

Dmitriy2003 писал(а):
Mleha писал(а):но создать WebBrowser в основном потоке, и заставить его отрисовывать страницу в другом - это не возможно.

Mleha писал(а):Тогда остается вопрос объединения разнопоточного GUI внутри одного контейнера

Зачем вообще это делать. :roll:
И специально для вас продолжение извращений с много поточной логикой, :evil:


Mleha писал(а):но создать WebBrowser в основном потоке, и заставить его отрисовывать страницу в другом - это не возможно.

Именно так делать мне и не нужно.

Control.CheckForIllegalCrossThreadCalls = False отключение потокобезопасности позволяет добавить часть UI из другого потока в UI основного.
Потоки остаются разделенными, но повисание дочернего потока цепляет и основной поток. И все приложение становится недоступным.

Dmitriy2003
Постоялец
Постоялец
 
Сообщения: 690
Зарегистрирован: 27.05.2003 (Вт) 22:47
Откуда: Deutschland

Re: Многопоточный UI

Сообщение Dmitriy2003 » 23.02.2011 (Ср) 14:18

Mleha писал(а):Потоки остаются разделенными, но повисание дочернего потока цепляет и основной поток. И все приложение становится недоступным.

Ну покажи, что там у тебя подвисает, и где конкретно. :) А то уже мнение складывается, что ты симуляции инсульта делаешь, :|

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Re: Многопоточный UI

Сообщение Mleha » 23.02.2011 (Ср) 16:08

Dmitriy2003 писал(а):
Mleha писал(а):Потоки остаются разделенными, но повисание дочернего потока цепляет и основной поток. И все приложение становится недоступным.

Ну покажи, что там у тебя подвисает, и где конкретно. :) А то уже мнение складывается, что ты симуляции инсульта делаешь, :|

В твоем последнем вложении добавил на дочернюю форму кнопку, которая запускает бесконечный цикл. Этим эмулирую повисание дочернего потока
Если не использовать SetParent, тогда повисание дочернего потока никак не сказывается на основном.
Вложения
wfa1-2.rar
(53.7 Кб) Скачиваний: 179

Dmitriy2003
Постоялец
Постоялец
 
Сообщения: 690
Зарегистрирован: 27.05.2003 (Вт) 22:47
Откуда: Deutschland

Re: Многопоточный UI

Сообщение Dmitriy2003 » 23.02.2011 (Ср) 19:42

Mleha писал(а):В твоем последнем вложении добавил на дочернюю форму кнопку, которая запускает бесконечный цикл. Этим эмулирую повисание дочернего потока

Гениально, жаль MIT покинул форум, он умел на брайнфаке писать - просто по другому уже мысли и не выразить то. :)

По делу: Раз вы такой привередливый и судя по всему из ваших рассуждений повиснуть может все и вся, а гуи должен жить вечно, может тогда предварительно грузить документ в веб браузер скрытый в отдельном потоке (класс вроде никуда не делся и по прежнему жив), затем вы ставите тайм аут и ждете гадостей, а при отсутствии таковых можете передать загруженный (готовый) документ в свой основной поток с гуем. Вот вроде как-то так. Ну а если и после этого ваш веб браузер вдруг повиснет - можно написать в тех поддержку microsoft - наверняка помогут. :) Написать такую абстракцию будет совсем не сложно - это я вам гарантирую, поверьте сэкономите кучу времени. :wink:

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

NashRus
Постоялец
Постоялец
 
Сообщения: 386
Зарегистрирован: 18.03.2006 (Сб) 1:16

Re: Многопоточный UI

Сообщение NashRus » 23.02.2011 (Ср) 23:48

Есть же реализации браузеров где каждая страница/вкладка в отдельной песочнице крутится.
Так и последние IE работают. Только там песочницы в виде процессов, а не потоков реализованы.
Может стоит посмотреть как сделано, например, в Хроме или Мозилле?

netdemon
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 179
Зарегистрирован: 04.09.2007 (Вт) 15:51

Re: Многопоточный UI

Сообщение netdemon » 01.08.2011 (Пн) 20:39

А ещё можно основной поток спроектировать как службу сервера приложения, А все другие кусочки будут клиентами этого сервера. И пускай себе виснут. :D
Лишь разум потерянный бесповоротно мною. Наполнить может сердце мне тоской.
Нельзя обнять необъятное и впихнуть невпихуемое.

Mleha
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 146
Зарегистрирован: 06.10.2004 (Ср) 7:49
Откуда: Ангарск

Re: Многопоточный UI

Сообщение Mleha » 29.04.2012 (Вс) 18:36

Наконец дошли руки, реализовал все в отдельных процессах. Для передачи сообщений использую пайпы. За основу взял пример на майкрософте)
Вот, может кому пригодится)
Полной безглючности не обещаю, но вроде все работает)
Вложения
Pipe.rar
Классы для межпроцессного взаимодействия на пайпах
(6.41 Кб) Скачиваний: 223


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7

    TopList  
cron