Чудеса в решете

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Чудеса в решете

Сообщение Brainenjii » 07.11.2010 12:53:53

Не могу понять, что происходит...
Есть класс отчёта (BReportClass), есть список, содержащий все отчёты. Список может пополняться при старте программы, или при создании нового отчёта. Вот эти процедуры:
Код: Выделить всё
Procedure GetReports; // загрузка всех отчётов
Begin
  With BQueryClass.Build Do
    Begin
      Get('SELECT * FROM REPORTS_INDEX ORDER BY ID');
      While Not(EOF) Do
        Begin
          ReportsList.Add(BReportClass.Build(Fields.ByNameAsInteger['ID'],
            Fields.ByNameAsInteger['GRADE'], Fields.ByNameAsString['CAPTION'],
            Fields.ByNameAsString['SUBJECT']));
          Next;
        End;
      Burn;
    End;
End;

Код: Выделить всё
Procedure TAppThread.AddReport(Const aReport: BReportClass);
Var
  i: Integer;
Begin
  WriteLn('we are here 0.o');
  If aReport = nil Then Exit;
  If ReportsList.IndexOf(aReport) = -1 Then ReportsList.Add(aReport);
  FormMain.StoreGrades.Clear;
  For i := 0 To GradesList.Count - 1 Do
    FormMain.StoreGrades.AddEntry(BGradeClass(GradesList[i]).JSON);
End;

Вот сам старт программы:
Код: Выделить всё

  GetGrades;
  GetReports;
  WriteLn(Format('ReportsList.Count: %d', [ReportsList.Count]));
  GetPupils;
  WriteLn(Format('ReportsList.Count: %d', [ReportsList.Count]));
  GetExams;
  WriteLn(Format('ReportsList.Count: %d', [ReportsList.Count]));
  GetMarks;
  WriteLn(Format('ReportsList.Count: %d', [ReportsList.Count]));

В выводе при запуске:
ReportsList.Count: 3
ReportsList.Count: 3
ReportsList.Count: 3
ReportsList.Count: 23

Логично предположить, что лишние записи появляются в процедуре GetMarks:
Код: Выделить всё
Procedure GetMarks;
Var
  aMark: BMarkClass;
  aExam: BExamClass;
Begin
  aExam := nil;
  With BQueryClass.Build Do
    Begin
      WriteLn(Format('1. ReportsList.Count: %d', [ReportsList.Count]));
      Get('SELECT * FROM MARKS_INDEX ORDER BY EXAM, ID');
      While Not(EOF) Do
        Begin
          WriteLn(Format('2. ReportsList.Count: %d', [ReportsList.Count]));
          If (aExam = nil) Or Not(aExam.ID=Fields.ByNameAsInteger['EXAM']) Then
            aExam := GetExam(Fields.ByNameAsInteger['EXAM']);
          WriteLn(Format('3. ReportsList.Count: %d', [ReportsList.Count]));
          aMark := BMarkClass.Build(Fields.ByNameAsInteger['ID'], aExam,
            GetPupil(Fields.ByNameAsInteger['PUPIL']),
            Fields.ByNameAsInteger['BALL']);
          WriteLn(Format('4. ReportsList.Count: %d', [ReportsList.Count]));
          aExam.Marks.Add(aMark);
          WriteLn(Format('5. ReportsList.Count: %d', [ReportsList.Count]));
          MarksList.Add(aMark);
          WriteLn(Format('6. ReportsList.Count: %d', [ReportsList.Count]));
          Next;
          WriteLn(Format('7. ReportsList.Count: %d', [ReportsList.Count]));
        End;
      Burn;
    End;
end;

При этом в консоли появляется вообще что-то невообразимое!!!
ReportsList.Count: 3
ReportsList.Count: 3
ReportsList.Count: 3
1. ReportsList.Count: 3
2. ReportsList.Count: 3
3. ReportsList.Count: 3
4. ReportsList.Count: 3
5. ReportsList.Count: 3
6. ReportsList.Count: 4
7. ReportsList.Count: 4
2. ReportsList.Count: 4
3. ReportsList.Count: 4
4. ReportsList.Count: 4
5. ReportsList.Count: 4
6. ReportsList.Count: 5
7. ReportsList.Count: 5
2. ReportsList.Count: 5
3. ReportsList.Count: 5
4. ReportsList.Count: 5
5. ReportsList.Count: 5
6. ReportsList.Count: 6
7. ReportsList.Count: 6
...

Иными словами, лишнее значение в ReportsList появляется на 6 "шаге": MarksList.Add(aMark);
Но MarksList и ReportsList - совсем разные списки!
Код: Выделить всё
Unit BMarkUnit;
...
Initialization
Begin
  MarksList := TList.Create;
End;

Код: Выделить всё
Unit BReportUnit;
...
Initialization
Begin
  ReportsList := TList.Create;
End;

Подскажите, что происходит? ^_^
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Чудеса в решете

Сообщение Odyssey » 07.11.2010 13:44:11

Я бы попробовал вместе с ReportsList.Count помониторить MarksList.Count. Если они всегда одинаковы, искал бы точки использования MarksList. Есть ли уже для этого рефакторинг -- не помню, можно поступить дедовским способом -- переименовать переменную в объявлении например на MarksList1 и пройтись по ошибкам. Ну или поиском.
Плюс можно было бы посмотреть ClassName элементов каждого списка.

Кроме того, меня смущает обращение из потока к полям формы и глобальным переменным без средств синхронизации:
Код: Выделить всё
Procedure TAppThread.AddReport
...
ReportsList.XXX
FormMain.StoreGrades.XXX

Если тредов более одного, будут проблемы.
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Чудеса в решете

Сообщение Brainenjii » 07.11.2010 13:55:13

Переименовал. MarksList встречается только в модуле, где описан BMarkClass (в сущности, он пока вообще не используется - задел на будущее, если возьмусь анализировать все оценки ^_^).
Экземпляр формы создаётся в потоке... Про общий список - да, пожалуй стоит выделить обращения к нему в секцию... Кстати, с потоками всё ещё только начинаю, - синхронизировать нужно любое обращение к списку, или чтение можно использовать и без оного?

UPD: закомментировал все вхождения MarksList - проблема исчезла... Но что это было?
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Чудеса в решете

Сообщение Odyssey » 07.11.2010 16:25:57

Brainenjii писал(а):Экземпляр формы создаётся в потоке...

Вот по этому поводу поспрашивать бы в рассылке. ЕМНИП, где то я читал что использование LCL не из главного потока может быть проблематичным, поскольку обработка сообщений крутится в главном потоке. Но в технические детали я не вдавался, и ссылку разумеется уже не найду.
Brainenjii писал(а):Кстати, с потоками всё ещё только начинаю, ...?

А я ещё не начинал, в ближайшее время не планирую, и безумно рад, что мне пока удаётся без них обходится :) Так что тут нужна будет ещё чья-то помощь.

UPD: закомментировал все вхождения MarksList - проблема исчезла... Но что это было?

Может быть удастся выделить простейший тестовый пример? Т.е. минимум сущностей на которых проблема воспроизводится? Два глобальных списка в разных модулях, тред с пустой формой, и т.п.? Тогда можно будет хотя бы кому-то ещё протестить, а впоследствии обратиться за комментариями в рассылку/багтрекер.
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Чудеса в решете

Сообщение Brainenjii » 07.11.2010 16:30:40

Боюсь что вряд ли... Такой схемой (модуль для класса, в нём процедура загружающая все актуальные экземпляры при старте и со списком, создающимся в Initialization) я пользуюсь уже очень давно и ни разу ничего подобного не замечал... И это не LCL, это http://bschool.h1.ru/ ^_^
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Чудеса в решете

Сообщение Odyssey » 07.11.2010 16:32:11

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

P.S. контент внутри iframe не открывается ("Попытка соединения не удалась").
Последний раз редактировалось Odyssey 07.11.2010 16:36:25, всего редактировалось 1 раз.
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Чудеса в решете

Сообщение Brainenjii » 07.11.2010 16:36:17

Понятно. Что ж, будем синхронизировать ^_^ Вообще, надо бы сделать шаблон синхронизированного списка, в идеале ещё и с индексированием и быстрой сортировкой по ID полям элементов списка... Вот только выходные уже прошли...
P.S. уже должно работать 0.о
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Чудеса в решете

Сообщение Odyssey » 07.11.2010 16:42:18

Теперь да. Занятно, и рисует вполне шустро, только грузится долго в первый раз.
И после изменения класса добавляет в список классы, созданные другими пользователями. Но это, наверное, work in progress, поэтому временно.
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Чудеса в решете

Сообщение Brainenjii » 07.11.2010 16:50:40

И точно ^_^ Поправил. В первый раз грузится ядро ExtJS фреймворка (~800кб) потом вроде как должно браться из кеша... Правда Indy с кешем работает как-то странно - иногда не возвращает картинки, например ^_^
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Re: Чудеса в решете

Сообщение Mr.Smart » 07.11.2010 17:14:27

Для работы в с потоками придумали класс TThreadList :wink:
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Чудеса в решете

Сообщение Brainenjii » 07.11.2010 18:28:31

и точно, вполне подойдёт ^_^ Правда не решает проблему приведения типов и не делает сортировки по полям... В общем, пойду модифицировать библиотеку уважаемого hinst'a ^_^
Аватара пользователя
Brainenjii
энтузиаст
 
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46


Вернуться в Free Pascal Compiler

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

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

Рейтинг@Mail.ru