Говорят, многопоточность 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 формы, позволяет циклически замораживать форму на произвольный коэффициент времени (имитируя её занятость сложными прорисовками) и т.д.