![]() |
|
![]() ![]() ![]() |
|
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Помогите решить задачку:
Есть класс отвечающий за ведение журнала (лога). Работает он следующим образом: - в процессе инициализации класса ему передаются указатели на объекты "подписчики" - когда вызывается функция регистрации нового сообщения поочередно вызывается функция для каждого подписчика. В эту функцию передается ссылка на структуру с данными сообщения. В моем случае имеется один подписчик (класс TForm, для этого он наследуется он класса "подписчика"). Функция OnLogAdd(...) соответственно вызывается при регистрации нового сообщения в журнале. Ее задача отображать на экране все сообщения (аля консоль). Для отображения я использую TTree. В программе могут стартовать и останавливаться потоки выполняющие некоторые вычисления. И основной поток и вспомогательные ведут журнал при помощи глобального объекта (того самого класса журнала). Сам класс вродебы потокобезопасный, во всяком случае с его стороны глюков не ловил. Проблема заключается в том, что программа периодически зависает. Причиной зависаний 99% работа с TTree, во всяком случае закоментировав работу с ним я не поймал ни одного зависания. Помогите как организовать потокобезопасный вывод? Попробовал такой вариант: в классе TForm написал функцию void Update() в которую вынес все что касается обновления дерева с журналом. в функцию OnLogAdd(...) поместил только сохранение копии сообщения (которая далее используется в Update() В потоке, после записи сообщения в журнал, принудительно вызываю Synchronize(Form1->Update); Так же после записи сообщения в журнал в основном потоке вызываю Update() Все отлично работает за исключением того, что пихать повсюду Update() ИМХО некорректно. И потом нет гарантий, что пока будет вызываться Update не будет перезаписана копия сообщения, т.е. реально можно просто потерять сообщение в журнале. Может я немного путанно написал, ежели что непонятно, спрашивайте. Мне очень важно найти решение этой задачи! Надеюсь на Вашу помощь!
|
|||
|
||||
Artemon |
|
|||
а ты мне нравишься ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1771 Регистрация: 24.2.2004 Где: Челябинск Репутация: 7 Всего: 20 |
1. Если обращаешься к визуальным компонентам VCL, то обязательно нужно использовать Synchronize, что ты и сделал и от этого никуда не уйти.
2. Я так понял у тебя один объект и его могут менять все, на этот случай можно воспользоваться мнтодами синхронизации, например Mutex или Семафор. -------------------- Контроль топлива на топливозаправщиках, мониторинг автотранспорта, расчет зарплаты водителей www.rscat.ru |
|||
|
||||
Lazin |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 18 Всего: 154 |
без Synchronize никуда, так как компоненты VCL используют очередь сообщений
|
|||
|
||||
_Tanatos_ |
|
||||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Не имею ничего против Synchronize, но как его грамотно использовать в моем случе?
Вот фрагмент исходного кода:
В результате
Причем нет гарантии, что реальный вызов не будет таким: main_thread LogHelper.Add() << "ThreadTest Main" << loPost; thread_1 LogHelper.Add() << "ThreadTest sub" << loPost; thread_1 Synchronize(Form1->Update); main_thread Update(); в результате получим две одинаковые строчки: 12 "ThreadTest sub" 12 "ThreadTest sub" вместо правильных 11 "ThreadTest Main" 12 "ThreadTest sub" |
||||
|
|||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 48 Всего: 223 |
Тебе нужна очередь в классе логера, в которую будут складываться сообщения от источников. Доступ к очереди должен быть синхронизирован. Порядок действий (функция OnLogAdd): 1) Блокируется очередь (критической секцией или семафором, неважно) 2) Сообщение добавляется в конец очереди 3) Разблокируется очередь 4) Вызывается Synchronize(Stage2) Фуннкция Stage2 (в классе логера): 1) Блокируется очередь 2) Из очереди вычитываются ВСЕ содержащиеся в ней сообщения, для каждого вызывается callback от подписчиков. (после этого очередь пуста) 3) Разблокируется очередь Для очереди можно применить класс deque из stl |
|||
|
||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Функция OnLogAdd и есть callback-подписчик! Если я правильно понял, то написанное Вами есть точно то что я и имею сейчас с одним лишь отличием добавлена очередь сообщений а не одно сообщение. Хорошо, проблему с очередностью это решит, а как быть с тем что каждый раз при добавлении записи в журнал надо принудительно вызывать дополнительную функцию для обновления интерфейса. Нелогично это, тем более что к журналу это никакого отношения не имеет. ИМХО в идеале надо решить эту проблему в пределах функции OnLogAdd, вот бы вызывать Synchronize(Update()); именно из нее, но она относится к основному потоку, а там нет никаких Synchronize. |
|||
|
||||
xvr |
|
||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 48 Всего: 223 |
Пардон, я имел в виду функцию по добавлению записи в лог.
Это и есть одно из 2х главных ее отличий.
Это второе отличие - она должна САМА вызывать эту функцию обновления (п. 4)
Поместите весь логер в отдельный поток и вызывайте наздоровье ![]() Или скопируйте реализацию метода Synchronize из TThread к себе в логер. |
||||||
|
|||||||
_Tanatos_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 28 Регистрация: 20.11.2007 Репутация: нет Всего: нет |
Интересно, где ты видел реализацию этой функции? А подвязывать класс журнала на VCL однозначно неправильно!!!, так как этот класс должен быть независим ни от каких библиотек кроме std Добавлено через 3 минуты и 4 секунды ИМХО Класс журнала должен безопасно регистрировать новое сообщение (пусть будет очередь сообщений) Затем от должен поочередно безопасно вызвать функци OnLogAdd Затем уже в пределах функции OnLogAdd должно быть выполнено безопасное действие про сохранению, отображению и т.д. сообщения. Вопрос в том, что внутри метода Synchronize, может буть это можно сделать самомму в пределах функции OnLogAdd() ? |
|||
|
||||
xvr |
|
||||||||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 48 Всего: 223 |
В исходниках vcl
Тогда его клиенты должны быть готовы к тому, что их будут вызывать на любых потоках, к чему vcl не готов.
Это зависит от того, сколько будет этих OnLogAdd. Если придется делать много разновидностей OnLogAdd с синхронизацией, то лучше ее поместить в сам логер, или сделать какой-нибдь wrapper для синхронизации
Посмотри сам в файле Source\vcl\classes.pas Собственно можно сделать и проще (так было сделано в BCB 3) - Synchronize посылала спец. сообщение в handle Application, по которому та в оконной процедуре вызывала запрошенную процедуру (теперь спец. сообщения нет, а производится переодический контроль на сообщении WM_NULL) (от BCB 6.0)
|
||||||||||||
|
|||||||||||||
![]() ![]() ![]() |
Правила форума "С++ Builder" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rrader. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C++ Builder | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |