Форум программистов, компьютерный форум, киберфорум
Delphi: Базы данных
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.64/11: Рейтинг темы: голосов - 11, средняя оценка - 4.64
0 / 0 / 0
Регистрация: 02.10.2018
Сообщений: 1
RAD XE3+

UniDac MySQL + потоки = access violation

02.10.2018, 11:46. Показов 2525. Ответов 2
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени суток.

Столкнулся с проблемой при работе с БД MySQL из потоков через компоненты UniDAC. Приведу пример:

Есть главный поток, который запускает N-потоков обработки данных, которые должны результат своей деятельности записывать в БД. Выглядит это примерно так:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
var
 ....
 MyThreads: TObjectList<TMyThread>;
....
procedure TMain_F.FormCreate(Sender: TObject);
var ....
begin
...
 MyThreads:= TObjectList<TMyThread>.Create();
 MyThreads.OwnsObjects := True;
end;
....
procedure TMain_F.tmr1Timer(Sender: TObject);
var qres: string; i,n,count: integer;
 ...
begin
 tmr1.Enabled := False;
 
 Caption := ifelse( Working , 'Работает' , 'Остановлен' );
 
 if not Working then
  begin
   tmr1.Enabled := True;
   Exit;
  end;
...
qres := ReadStrBy('SELECT max_threads_count FROM `sys_data`');
....
// узнаем сколько процессов требуется запустить( значение берется из БД )... но можно и упростить для примера
count := qres - MyThreads.Count;
....
for i := 0 to count-1 do
 begin
  n := MyThreads.Add(TMyThread.Create(MySQLConnectionInfo)); 
 
  { MySQLConnectionInfo - глобальная переменная из другого модуля (  см. ниже) }
 
  MyThreads[n].FreeOnTerminate := True;
  MyThreads[n].Priority := tpLower;
  Application.ProcessMessages;
 end;
..
end;
....
procedure CheckMyThreads;
var i: integer;
begin
 for i := MyThreads.Count-1 downto 0 do
   if MyThreads[i] <> nil then
      begin
       if MyThreads[i].Finished then
        MyThreads.Delete(i);
      end else MyThreads.Delete(i);  
end;
.................................
function ReadStrBy(query: string; r: Integer = 0): string;
var I: Integer;
    res: Boolean;
    msg: string;
begin
 try
 
   I := r;
   repeat
    Inc(I);
    if I > 1 then Delay(300*I, Main_F.Handle);
    Result := SQL_ReadStrBy( query , res, msg);
   until res or (I > 7);
 
   if not res then
    begin
     AddToLog('Ошибка MySQL: ' + msg);
    end;
 
 except
  on E: Exception do
   begin
    AddToLog('MYSQL_EXCEPTION: '+E.Message);
   end;
 end;
end;
.........................
Сами методы работы с БД:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
unit utils...
....
    { ------- Функции и процедуры для работы с БАЗОЙ ДАННЫХ MySQL ------- }
    { ------------------------------------------------------------------- }
    // Получения строки для соединения с MySQL
    function GetConnectionString: string;
    ........
    // Возвращает все строки по указанному запросу
    function SQL_ReadStrBy(UniQuerySQLText: string; out Res: Boolean; out msg: string): string;
    // Выполнить запрос к БД MySQL
    procedure SQL_RunQuery(UniQuerySQLText: string; out Res: Boolean; out msg: string);
    .........
 
type
  TMySQLConnectionInfo = record
    Server: string[255];
    Port: Integer;
    Database,
    UserName,
    Password: string[255];
  end;
 
var
  ..........................
  MySQLConnectionInfo: TMySQLConnectionInfo;
 
const
  MySQLDelimiter = '^^^_mysql_^^^';
  MySQLRowDelimiter = '^^^_mysqlRow_^^^';
 
.................................................................
 
// Получения строки для соединения с MySQL
function GetConnectionString: string;
begin
 Result := 'Login Prompt=False;Provider Name=MySQL;Data Source=' + MySQLConnectionInfo.Server +
 ';Database=' + MySQLConnectionInfo.Database + ';User ID=' + MySQLConnectionInfo.UserName +
 ';Password=' + MySQLConnectionInfo.Password + ';Port=' + IntToStr(MySQLConnectionInfo.Port);
end;
 
// Возвращает все строки по указанному запросу
function SQL_ReadStrBy(UniQuerySQLText: string; out Res: Boolean; out msg: string): string;
var UniQuery: TUniQuery; SL: TStringList;
UniConnection: TUniConnection; I: Integer; S: string;
begin
 
 Result := ''; SL := TStringList.Create; Res := False; msg := '';
 try
  try
    UniConnection:= TUniConnection.Create(nil);
    UniConnection.ConnectString:= GetConnectionString;
 
    UniConnection.Server := MySQLConnectionInfo.Server;
    UniConnection.Port := MySQLConnectionInfo.Port;
    UniConnection.Username := MySQLConnectionInfo.UserName;
    UniConnection.Password := MySQLConnectionInfo.Password;
    UniConnection.Database := MySQLConnectionInfo.Database;
    UniConnection.SpecificOptions.Values['MySQL.CharSet'] := 'UTF8';
    UniConnection.SpecificOptions.Values['MySQL.UseUnicode'] := 'True';
 
    UniConnection.ProviderName:= 'MySQL';
    UniConnection.LoginPrompt:= false;
    UniQuery:= TUniQuery.Create(nil);
    UniQuery.Connection:= UniConnection;
    UniQuery.Close;
    UniQuery.SQL.Clear;
    UniQuery.SQL.Text:= UniQuerySQLText;
    UniQuery.Open;
    if not UniQuery.FindFirst then begin Res := True; Exit; end;
    repeat
       S := '';
       for I:=0 to UniQuery.Fields.Count-1 do
        S := S + StringReplace( UniQuery.Fields[I].AsString, #13#10, '\r\n', [rfReplaceAll]) +
         ifelse( (UniQuery.Fields.Count=1) or (I=UniQuery.Fields.Count-1) , ''{MySQLRowDelimiter}, MySQLDelimiter );
       SL.Add(S);
    until not UniQuery.FindNext;
    Result := SL.Text;
    if Length(Result)>0 then
     if Copy(Result,Length(Result)-1,2)=#13#10 then SetLength(Result,Length(Result)-2);
    Res := True;
  except
   on E:Exception do msg := E.Message;
  end;
 finally
  SL.Free;
  UniQuery.Close;
  UniQuery.Free;
  UniConnection.Close;
  UniConnection.Free;
 end;
end;
 
// Выполнить запрос в БД MySQL
procedure SQL_RunQuery(UniQuerySQLText: string; out Res: Boolean; out msg: string);
var UniQuery: TUniQuery; UniConnection: TUniConnection;
begin
 
 msg := '';
 Res := False;
 try
  try
    UniConnection:= TUniConnection.Create(nil);
    UniConnection.ConnectString:= GetConnectionString;
 
    UniConnection.Server := MySQLConnectionInfo.Server;
    UniConnection.Port := MySQLConnectionInfo.Port;
    UniConnection.Username := MySQLConnectionInfo.UserName;
    UniConnection.Password := MySQLConnectionInfo.Password;
    UniConnection.Database := MySQLConnectionInfo.Database;
    UniConnection.SpecificOptions.Values['MySQL.CharSet'] := 'UTF8';
    UniConnection.SpecificOptions.Values['MySQL.UseUnicode'] := 'True';
 
    UniConnection.ProviderName:= 'MySQL';
    UniConnection.LoginPrompt:= false;
    UniQuery:= TUniQuery.Create(nil);
    UniQuery.Connection:= UniConnection;
    UniQuery.Close;
    UniQuery.SQL.Clear;
    UniQuery.SQL.Text:= UniQuerySQLText;
    UniQuery.ExecSQL;
    //msg := IntToStr( UniQuery.LastInsertId );
    Res := True;
  except
   on E:Exception do msg := E.Message;
  end;
 finally
  UniQuery.Close;
  UniQuery.Free;
  UniConnection.Close;
  UniConnection.Free;
 end;
end;
И собственно пример самого потока:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...................................
type
  TMyThread = class(TThread)
  private
   ..................................
    // Получения строки для соединения с MySQL
    function GetConnectionString: string;
    // Возвращает все строки по указанному запросу
    function SQL_ReadStrBy(UniQuerySQLText: string; out Res: Boolean; out msg: string): string;
    // Выполнить запрос к БД MySQL
    procedure SQL_RunQuery(UniQuerySQLText: string; out Res: Boolean; out msg: string);
 
    procedure RunQuery(query: string; r: Integer = 0);
    function ReadStrBy(query: string; r: Integer = 0): string;
  ..................
  protected
    procedure Execute; override;
  public
    ...................................
    MySQLCInfo: TMySQLConnectionInfo;
    constructor Create(CI: TMySQLConnectionInfo);
    destructor Destroy; override;
 
............................................
 
constructor TMyThread.Create(CI: TMySQLConnectionInfo);
begin
 inherited Create;
 ..........................
 MySQLCInfo := CI;
  ......................
end;
 
procedure TMyThread.Execute;
...................
begin
 // делаем какие-то расчёты
 ........................................
 // пишем результат в БД, например
 if ERROR then
  begin
   RunQuery('UPDATE `table` SET `field` = ''FAIL'' ');
   Terminate;
  end else
  begin
   RunQuery('UPDATE `table` SET `field` = ''VALUE'' ');
   // продолжаем расчёты
   .................
   // пишем что-то еще в БД.....
  end;
end;
В итоге с некоторой периодичностью получаю AV( Access violation ) в методе ReadStrBy и RunQuery. То есть у одного из 100 потоков, а иногда и у 20 из 100... Причем до этого вместо потоков запускались отдельные процессы - результат был тот же - AV возникает именно в методах Run или Read работы с БД. Отследить подробнее не получилось - так как возникает только при большом количестве процессов/потоков. Так вот собственно вопрос, если AV возникает в них - значит, SQL_Read/Run отрабатывают без ошибок... Но дело то наверняка именно в них, но почему то обработчик исключительной ситуации вызывается не в них, а методом выше?

P.S> Не уверен, что пишу в тот раздел, возможно стоило написать в раздел БД, если что прошу модераторов извинить и перенести в соответствующий раздел.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
02.10.2018, 11:46
Ответы с готовыми решениями:

Delphi XE + UniDac + Mysql
привет Я хочу спросить, как реализовать доступ пользователей к программе? Я использую Delphi XE - UniDAC - MySQL. делаю следующее: ...

UniDAC (mysql) построение запроса
Есть проблема с удаленной базой. Периодически выполнение запроса приводит к ошибке типа нет такого поля. С локальной базой такая проблема...

MySql+Delphi+dbGrid+uniDAC
Как вывести в dbGrid данные из БД MySql и с использованием компонента uniDAC. У меня вот стандартными компонентами получалось...

2
882 / 404 / 173
Регистрация: 20.10.2016
Сообщений: 1,828
02.10.2018, 13:21
Налицо непонимание принципа работы с потоками.

Delphi
1
MyThreads.OwnsObjects := True;
Вот этого делать не стоит. Поток - это не совсем обычный объект, имхо, лучше завершать вручную и внутри самого потока

Delphi
1
2
3
4
5
6
n := MyThreads.Add(TMyThread.Create(MySQLConnectionInfo)); 
 
  { MySQLConnectionInfo - глобальная переменная из другого модуля (  см. ниже) }
 
  MyThreads[n].FreeOnTerminate := True;
  MyThreads[n].Priority := tpLower;
Поток создается suspended или не suspended? Вообще, всю инициализацию, типа установки FreeOnTerminate, нужно проводить внутри конструктора потока, но никак не извне этого потока, тем более, если он не засуспенден.

Delphi
1
2
3
4
5
6
7
8
9
10
procedure CheckMyThreads;
var i: integer;
begin
 for i := MyThreads.Count-1 downto 0 do
   if MyThreads[i] <> nil then
      begin
       if MyThreads[i].Finished then
        MyThreads.Delete(i);
      end else MyThreads.Delete(i);  
end;
Нельзя так! Удалять потоки извне, тем более таким образом - это табу. Обращаться к переменной потока извне - табу! Внутри потока в его методе Execute надо проверять
Delphi
1
if self.Finished then exit;
и поток сам завершится и удалится, так как у нас FreeOnTerminate установлен в true.


Теперь к самому потоку:

Delphi
1
2
3
4
5
6
7
8
9
10
constructor TMyThread.Create(CI: TMySQLConnectionInfo);
begin
 inherited Create;
 ..........................
 MySQLCInfo := CI;
  ......................
//перенести сюда вот эти строки:
  self.FreeOnTerminate := True;
  self.Priority := tpLower; //почему Lower? Он так работать вообще не будет. Я бы эту штуку не трогал без глубокого понимания что произойдет
end;
Далее.

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
procedure TMyThread.Execute;
...................
begin
 // делаем какие-то расчёты
 ........................................
 // пишем результат в БД, например
 if ERROR then
  begin
   RunQuery('UPDATE `table` SET `field` = ''FAIL'' ');
   Terminate;
  end else
  begin
   RunQuery('UPDATE `table` SET `field` = ''VALUE'' ');
   // продолжаем расчёты
   .................
   // пишем что-то еще в БД.....
  end;
end;
Поток пройдет единожды этот метод и завершится. Нигде не вижу цикла, который бы заставлял поток продолжать работать. В свете этого мне непонятно применение Terminate. Этот метод просто переводит Terminated из false в true, и ничего более. Поток он не уничтожает. Уничтожится поток после завершения метода Execute (не совсем так, но в свете такого кода такое допущение можно сделать).

Итог: читайте про потоки. Поток - это как партизан. Вы даете ему задание, он прикладывает руку к козырьку и убывает на выполнение. Трогать его во время выполнения задачи нельзя, возвращаться на базу он может только через синхронизацию. После выполнения задания партизан заканчивает жизнь самоубийством.
0
03.10.2018, 14:46

Не по теме:

Цитата Сообщение от Nanotentacle Посмотреть сообщение
После выполнения задания партизан заканчивает жизнь самоубийством.
:bravo:
))

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
03.10.2018, 14:46
Помогаю со студенческими работами здесь

Подключение к Access используя uniDAC
Как подключится к Аксесовской базе которая находится по адресу C:/1.mdb используя uniDAC? Добавлено через 17 минут к mdb...

Получить список объектов БД MS Access используя UniDAC
C помощью UniConnection подключился к БД. В UniMetaData указал Connection=UniConnection. Кинул на форму также AccessUniProvider. Как мне...

Access violation
Выбивает вот такую ошибку , до этого ее не было не могу понять почему. Происходит при попытке взять данные из DBGrid, для примера...

Access violation
При выходе из программы, и то не всегда, как исправить , где?

Access violation at address
Прописал код добавления в IB БД через IBQuery и возникла вышеприведённая ошибка, хотя добавление срабатывает. Также и с удалением. В чём...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
3
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru