|
Рисование из потока |
|
|
|
Nov 18 2008, 06:45
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(hadrov @ Nov 18 2008, 02:14)  Ага, теперь понял откуда ноги ростут. Эта процедура из TThread. При создании модуля с классом TThread и написано, что нельзя рисовать из потока, кроме как используя метод synchronize. Позже попробую написать его потомок, а пока я потоки реализованы с помощью API. В этом случае есть возможность рисовать из потока в главном окне? Что бы не действовать методом научного тыка приведу краткий алгоритм, а то не уверен, что правильно Вас понял. Поток приема из УАРТа. Код 1. Ждем стартовый байт. (WaitCommEvent(), WaitForSingleObject()) 2. Входим в критическую секцию. (EnterCriticalSection()) 3. Записываем данные в буфер. (ReadFile()) 4. Выходим из критической секции. (LeaveCriticalSection()) 5. Устанавливаем событие в сигнальное состояние (событие с автосбросом) (SetEvent()) 6. Переходим к п. 1. Поток обработки данных Код 1. Ждем наступления события. (WaitForSingleObject()) 2. Входим в критическую секцию. 3. Извлекаем данные из буфера. 5. Выходим из критической секции. 6. Переходим к п. 1. Да, именно так Цитата И как после извлечении данных из буфера и выхода из критической секции вставить пункт "Рисуем в главном окне", чтобы он отработал нормально? Посадить потоки на TThread, или вручную реализовать то, что сделано в TThread::Synchorize. А именно - - Данные помещаются в очередь (можно просто переменная)
- Главному окну посылается специальное сообщение (любое)
- WaitForSingleObject(sync_event)
В обработчике специального сообщения главного окна: - Забираем данные, рисуем
- SetEvent(sync_event)
Event sync_event должен быть создан как Код sync_event=CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
|
|
|
Nov 18 2008, 07:40
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(hadrov @ Nov 18 2008, 04:14)  Поток приема из УАРТа. ... Не так. Поток приема из УАРТа. - Ждем стартовый байт. (WaitCommEvent(), WaitForSingleObject())
Входим в критическую секцию. (EnterCriticalSection()) - не надо, больше никто всё равно не читает из порта.- Записываем данные в буфер. (ReadFile())
Выходим из критической секции. (LeaveCriticalSection())- Распределяем память под принятое количество байтов и копируем туда принятое;
- Отправляем указатель на этот блок памяти в главное окно при помощи PostMessage(WM_USER+3);
Устанавливаем событие в сигнальное состояние (событие с автосбросом) (SetEvent()) - это тоже лишнее, поскольку мы отправили сообщение сразу в главное окно и никто не спит, дожидаясь данных.- Переходим к п. 1.
Основной поток программы: - Ждем сообщения WM_USER+3;
- Берём данные из указателя - параметра, отрисовываем их.
- Уничтожаем память под указателем.
- Переходим к п. 1.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 18 2008, 09:25
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(AHTOXA @ Nov 18 2008, 10:40)  Не так. Поток приема из УАРТа. - Ждем стартовый байт. (WaitCommEvent(), WaitForSingleObject())
Входим в критическую секцию. (EnterCriticalSection()) - не надо, больше никто всё равно не читает из порта.- Записываем данные в буфер. (ReadFile())
Выходим из критической секции. (LeaveCriticalSection())- Распределяем память под принятое количество байтов и копируем туда принятое;
- Отправляем указатель на этот блок памяти в главное окно при помощи PostMessage(WM_USER+3);
Устанавливаем событие в сигнальное состояние (событие с автосбросом) (SetEvent()) - это тоже лишнее, поскольку мы отправили сообщение сразу в главное окно и никто не спит, дожидаясь данных.- Переходим к п. 1.
Основной поток программы: - Ждем сообщения WM_USER+3;
- Берём данные из указателя - параметра, отрисовываем их.
- Уничтожаем память под указателем.
- Переходим к п. 1.
Частые заказы/освобождения блоков памяти череваты возростанием фрагментации кучи, что может привести к перерасходу памяти и общим тормозам для всей программы. Все таки рекомендуется сделать кольцевой буффер. Да, и делать 2 дополнительных потока это тоже перебор, вполне хватит и одного - для приема. Обработку и отрисовку можно делать в главном потоке приложения.
|
|
|
|
|
Nov 18 2008, 09:46
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(XVR @ Nov 18 2008, 14:25)  Частые заказы/освобождения блоков памяти череваты возростанием фрагментации кучи, что может привести к перерасходу памяти и общим тормозам для всей программы. Все таки рекомендуется сделать кольцевой буффер. Ерунда. Не те объёмы. Да и блоки скорее всего фиксированной длины. Цитата(XVR @ Nov 18 2008, 14:25)  Да, и делать 2 дополнительных потока это тоже перебор, вполне хватит и одного - для приема. Обработку и отрисовку можно делать в главном потоке приложения. Ну а я как написал?
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 18 2008, 10:32
|
Участник

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540

|
Цитата(AHTOXA @ Nov 18 2008, 13:46)  Ну а я как написал? Скорее всего, это адресовалось мне. Я сейчас сделал еще один тестовый вариант с использованием компоненты TComPort. Там нечто подобное внутри реализовано - крутится поток и при приеме данных возникает событие, данные сохраняются в буфере. Так вот из этого события я уже и рисую, и пока ошибок доступа и проблем с графикой не наблюдаю. Т.к. следующее предположение верно Цитата Да и блоки скорее всего фиксированной длины. , то можно ведь изменить пункт Цитата # Распределяем память под принятое количество байтов и копируем туда принятое; на "один раз выделили в начале и потом просто пользуемся этим указателем до конца работы". Попутно вопрос: сейчас данные приходят через фиксированные промежутки времени, данные успевают обработаться за это время, и поэтому, как я понимаю, хватает одной ячейки во втором буфере. А теперь представим ситуацию, когда данные приходят нерегулярно. Тут уже потребуется FIFO с достаточным количеством ячеек?
|
|
|
|
|
Nov 18 2008, 11:03
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(hadrov @ Nov 18 2008, 15:32)  Я сейчас сделал еще один тестовый вариант с использованием компоненты TComPort. Там нечто подобное внутри реализовано - крутится поток и при приеме данных возникает событие, данные сохраняются в буфере. Да, это как раз очень подходящий пример. Цитата Т.к. следующее предположение верно, то можно ведь изменить пункт "Распределяем память под принятое количество байтов и копируем туда принятое;" на "один раз выделили в начале и потом просто пользуемся этим указателем до конца работы". Нет, так нельзя. Потому что пока первый блок обрабатывается (сначала стоит в очереди оконных сообщений, потом отрисовывается), второй и последующие блоки уже принимаются. В экстремальном случае (подвисание главного потока на несколько секунд) в очереди может скопиться несколько таких блоков. Так что либо так как я написал, либо, например, кольцевая очередь из заранее выделенных буферов, с передачей в сообщении номера (индекса) заполненного буфера. Но точно не один буфер. Цитата Попутно вопрос: сейчас данные приходят через фиксированные промежутки времени, данные успевают обработаться за это время, и поэтому, как я понимаю, хватает одной ячейки во втором буфере. А теперь представим ситуацию, когда данные приходят нерегулярно. Тут уже потребуется FIFO с достаточным количеством ячеек? В винде никогда нельзя закладываться на то, что программа успеет
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 18 2008, 11:20
|
Участник

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540

|
Цитата Нет, так нельзя. Теперь ясно. Я не так понял "Распределяем память под принятое количество байтов и копируем туда принятое". Почему то подумал, что это всегда будет одна и та область памяти. А тут мы каждый раз выделяем новую, но при этом указатели на старые куски данных могут быть еще валидными. Ну, и таким образом отпал вопрос о не успевании программы.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|