Штатная многопоточность VB6 – в деле

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
С.Т.
Новичок
Новичок
 
Сообщения: 45
Зарегистрирован: 10.03.2010 (Ср) 19:49

Штатная многопоточность VB6 – в деле

Сообщение С.Т. » 01.08.2025 (Пт) 23:04

Говорят, многопоточность ActiveX EXE нецелесообразна, т.к. ждёт при межпоточных вызовах, а оказывается (помимо того что на практике это не мешает) легко обойти эту неловкость с помощью общего указателя на общую структуру данных — любой обмен данными тогда происходит мгновенно, независимо от их объёма и загруженности потоков, и заодно получается набор «сверхглобальных» переменных, что удобно в программировании.

Проще использовать vbCreateThread для StandardEXE от The Trick и передать указатель на общую структуру туда в параметр, но если по каким-то причинам это не удаётся (например, соседство с глючными контролами, использующими TLS), приведу "железный" способ встроенной многопоточности ActiveX. (Бонус - защита программы от копирования, хоть и детская: при первом запуске она регистрирует COM-объект по заданному пути и после копирования уже так просто не запустится.)

Этот проект вырос на базе примера от Arthur2.

Я сразу переделал его в четыре проекта по нарастанию сложности, похожих на выложенные здесь.

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

Но ещё оставался недостаток:
1) для обновления общей переменной всякий раз требовался вызов CopyMemory
2) в случае со структурой это осложняется тем, что во время обновления переменной нужно знать смещение её графы в памяти или обновлять всю структуру, что избыточно;
3) затруднена обратная передача от главного потока к побочному. (В цикле побочного потока нужно всякий раз проверять, не пришло ли чего нового.)

И тут вспомнилась статья Хакера о простом способе получить Полноценный Указатель.
Всё! Все проблемы решены одним махом. В Основном Цикле побочного потока направляем ByRef-указатель на нашу общую структуру – и сверхглобальные переменные наши! Пишем здесь – читаем в другом потоке и обновляем – читаем здесь… всё что угодно! Массив Single на 40 000 000 индексов (!) обновляется за 0,00003 мс. Ничего удивительного: данные открытого массива-то физически не копируются! Выходит, и молниеносность обновления любых значений максимальная.

Тут-то я и решил поделиться этим проектом с сообществом. Я не владею теорией и не знаю, правильно ли это, но на практике этот подход мне понравился удобством и, как мне кажется, надёжностью (по крайней мере легко соседствует с капризным Bass.dll и многопоточными OCX, имеющими обыкновение сбивать TSL с толку).

Во вложении три примера:
1) Заготовка для многопоточной программы, с явным разделением внутреннего кода и пользовательского.

2) Простой тест двух потоков. Главный поток занимается отрисовкой формы и плавным смещением контрола (имитация плавной отрисовки графики). Побочный поток заполнен паразитным циклом, «порождающим» ответ: 1, потом 2, потом 3, потом 4 и т.д. Ответ всякий раз пересылается в главный поток. Но даже когда главный поток занят, побочный поток продолжает работать и генерировать ответы. Это проверяется нажатием на кнопку «Заморозить форму» - главный поток зависает в паразитном цикле на несколько секунд, а когда отвисает, мы видим, что цифры побочного потока успели за это время нарасти – стало быть, он не ждал освобождения формы, работал!

3) Реальный пример использования многопоточности. Отдельный поток делает снимок с веб-камеры (на плохих камерах каждый снимок тормозит поток на 100 — 200 мс!), увеличивает его (интерполяция тоже занимает время), дальше может преобразовывать как нужно программисту, и затем поставляет в Главный Поток в готовом виде. При этом Главный Поток занимается плавной отрисовкой 2D графики и совершенно не вникает в процесс производства снимков. Тестовый проект первично отлажен, показывает FPS камеры, FPS формы, позволяет циклически замораживать форму на произвольный коэффициент времени (имитируя её занятость сложными прорисовками) и т.д.
Вложения
Сверхглобальные переменные в штатной многопоточности VB6.zip
(122.34 Кб) Скачиваний: 6
Последний раз редактировалось С.Т. 05.08.2025 (Вт) 13:45, всего редактировалось 4 раз(а).

С.Т.
Новичок
Новичок
 
Сообщения: 45
Зарегистрирован: 10.03.2010 (Ср) 19:49

Re: Штатная многопоточность VB6 – в деле

Сообщение С.Т. » 01.08.2025 (Пт) 23:18

Это проект скорей ознакомительный (сам-то буду встраивать TrickMultiThreading), но вопросов по этой теме встречал гораздо больше, чем ответов, и решил ответить (и себе, и другим) рабочими примерами.

Но ещё я что заметил, поработав денёк с штатной многопоточностью. Она неожиданно удобна для любительского программирования. Как вообще весь язык VB6. Все общие переменные копируются в новый поток. Избыточно? Зато удобно! Не надо делать новых названий, обращаешься как привык, вызываешь общие для обоих потоков процедуры, использующие глобальные переменные, и не думаешь, где что наслоится. Просто учитываешь, что в другом потоке значения могут устареть, и обновляешь нужные Public’и через отдельную процедуру в нужный момент.

Первый запуск от админа я не считаю недостатком, потому что если уж программа требует многопоточности, наверняка у ней уже целая гора DLL и OCX, так что она вообще требует установщика. (К тому же, на ломаных виндах часто нет msvbvm60.dll)

А вот недостаток всё же есть: при первом запуске (даже после установки) антивирусы с
неуверенностью относятся к "созданию COM объекта" и "изменению реестра", хотя установщику это всё позволяют безропотно. Тут вообще даже приходит вопрос, а нельзя ли установщику поручить создать COM объект в реестре. (HKLM/Software/Classes/ имя программы и в CLSID её код и там путь к программе и всё такое. Но сам CLSID ведь генерируется не с потолка, а по какому-то алгоритму.)

И может, в новых "суперзащищённых" виндах эти ActiveX подстерегают ещё какие-то неприятности?..

The trick
Постоялец
Постоялец
 
Сообщения: 804
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Штатная многопоточность VB6 – в деле

Сообщение The trick » 02.08.2025 (Сб) 9:55

С.Т. писал(а):Это проект скорей ознакомительный (сам-то буду встраивать TrickMultiThreading), но вопросов по этой теме встречал гораздо больше, чем ответов, и решил ответить (и себе, и другим) рабочими примерами.

Но ещё я что заметил, поработав денёк с штатной многопоточностью. Она неожиданно удобна для любительского программирования. Как вообще весь язык VB6. Все общие переменные копируются в новый поток. Избыточно? Зато удобно! Не надо делать новых названий, обращаешься как привык, вызываешь общие для обоих потоков процедуры, использующие глобальные переменные, и не думаешь, где что наслоится. Просто учитываешь, что в другом потоке значения могут устареть, и обновляешь нужные Public’и через отдельную процедуру в нужный момент.

Первый запуск от админа я не считаю недостатком, потому что если уж программа требует многопоточности, наверняка у ней уже целая гора DLL и OCX, так что она вообще требует установщика. (К тому же, на ломаных виндах часто нет msvbvm60.dll)

А вот недостаток всё же есть: при первом запуске (даже после установки) антивирусы с
неуверенностью относятся к "созданию COM объекта" и "изменению реестра", хотя установщику это всё позволяют безропотно. Тут вообще даже приходит вопрос, а нельзя ли установщику поручить создать COM объект в реестре. (HKLM/Software/Classes/ имя программы и в CLSID её код и там путь к программе и всё такое. Но сам CLSID ведь генерируется не с потолка, а по какому-то алгоритму.)

И может, в новых "суперзащищённых" виндах эти ActiveX подстерегают ещё какие-то неприятности?..

Разницы между ActiveX EXE и ActiveX DLL нет в плане многопоточности. Нужна многопоточность без всяких установок - переноси многопоточный код в ActiveX DLL. Я уже давал тут пример. Там же я написал что можно потоки запускать в отдельных процессах. Для этого вообще никаких зависимостей не нужно.
UA6527P

С.Т.
Новичок
Новичок
 
Сообщения: 45
Зарегистрирован: 10.03.2010 (Ср) 19:49

Re: Штатная многопоточность VB6 – в деле

Сообщение С.Т. » 02.08.2025 (Сб) 19:06

Ух ты, я там не увидел, спасибо! Изучаю!
(Вообще, пообщавшись с вами и написав в качестве упражнения эти примеры, я начал понимать почти всё, что Вы пишете в статьях о Многопоточности! Раньше не понимал там ни слова.)

Через отдельный ПРОЦЕСС я уже пробовал снимать кадры (давно), на основе Worker-движка от Olaf. Очень большие задержки кадров, даже на глазок. Приходится физически копировать каждый кадр (с указателями не получилось: при разных программах они указывают чёрти-как). Причём задержки гораздо больше, чем в ActiveX EXE даже при таком же физическом копировании (первом несовершенном алгоритме) - видимо, межпроцессный маршалинг сильно туже межобъектного. (Может, чушь сказал, но практика показывает что-то подобное.)
А теперь, с указателями, ActiveX сменяет кадры молниеносно и в этом смысле не отличатся от прямого vbCreateThread. (Проверяется резкими разжатиями кулака при флажке "Показать OCX" и сравнением отклика на ОСХ и на форме.)

А вот перенос второго потока в ActiveX DLL - идея интересная! Надо будет сделать и проверить, не будет ли задержек, как при отдельном процессе, и можно ли использовать общий указатель.


Вернуться в Visual Basic 1–6

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

Сейчас этот форум просматривают: Google-бот и гости: 2

    TopList  
cron