I am going to want to run in a multithreaded setup, and I'm doing some testing and find this isn't working.
Delphi XE7, and I'm using Firebird embedded, and according to their docs, 2.5 is threadsafe. I'm using Firebird 2.5.3.26780 32 bit embedded. And I have also downloaded and tried the latest snapshot of Zeos (zeoslib-code-0-3621-branches-testing-7.2) and that doesn't eliminate the errors.
I created a test program. Using a ZConnection and ZQuery. Then have created threads that I can launch by pressing a toolbutton. The Thread creates a new ZConnection and ZQuery inside the Execute method, attaching to the same database. Every half a second, the thread refreshes (all it does is calculate rowcount):
try
ZQuery_rowcount.Refresh;
except
on E:Exception do
begin
FCS.Enter;
try
FErrMsg := 'Refresh: [' + E.Message + ']';
finally
FCS.Leave;
end;
end;
end;
FCS.Enter;
try
try
FRowCount := ZQuery_rowcount.Fields[0].AsLargeInt;
except
on E:Exception do
FErrMsg := 'AsLargeInt: [' + E.Message + ']';
end;
finally
FCS.Leave;
end;
After I launch about 5 of these windows, I start getting errors. For example, the "row_count" field is jumbled as if the queries from the different threads are getting jumbled together and sent to Firebird. And of course it returns an error.
What makes me think the queries from the different threads aren't threadsafe is that two errors will occur at about the same time, one will have the "row_count" missing some letters with an error indicating that the field isn't found, and the other error will have those missing letters from the "row_count" from the first error.
Multithreading bugs
Multithreading bugs
Last edited by amarryat on 06.03.2015, 16:53, edited 2 times in total.
Re: Multithreading bugs
FYI, this is my exact Execute method in the Thread. FCS is a critical section that was created when the thread was created:
procedure TThread_DB.Execute;
var
Connection: TZConnection;
ZQuery_rowcount: TZQuery;
begin
try
Connection := TZConnection.Create(nil);
try
Connection.Database := FDatabase;
Connection.LibraryLocation := FLibraryLocation;
Connection.Protocol := FProtocol;
Connection.User := FUser;
Connection.Password := FPassword;
Connection.Connected := True;
ZQuery_rowcount := TZQuery.Create(nil);
try
ZQuery_rowcount.Connection := Connection;
ZQuery_rowcount.SQL.Add('select count(*) as num_rows from my_data');
ZQuery_rowcount.Active := True;
repeat
try
ZQuery_rowcount.Refresh;
except
on E:Exception do
begin
FCS.Enter;
try
FErrMsg := 'Refresh: [' + E.Message + ']';
finally
FCS.Leave;
end;
end;
end;
FCS.Enter;
try
try
FRowCount := ZQuery_rowcount.Fields[0].AsLargeInt;
except
on E:Exception do
FErrMsg := 'AsLargeInt: [' + E.Message + ']';
end;
finally
FCS.Leave;
end;
if not MessageLoop then
MsgWaitForMultipleObjects(1,
FSleepEvent,
false,
100,
QS_ALLINPUT);
until Terminated;
finally
ZQuery_rowcount.Free;
end;
finally
Connection.Free;
end;
except
on E:Exception do
begin
FCS.Enter;
try
FErrMsg := E.Message;
finally
FCS.Leave;
end;
end;
end;
end;
procedure TThread_DB.Execute;
var
Connection: TZConnection;
ZQuery_rowcount: TZQuery;
begin
try
Connection := TZConnection.Create(nil);
try
Connection.Database := FDatabase;
Connection.LibraryLocation := FLibraryLocation;
Connection.Protocol := FProtocol;
Connection.User := FUser;
Connection.Password := FPassword;
Connection.Connected := True;
ZQuery_rowcount := TZQuery.Create(nil);
try
ZQuery_rowcount.Connection := Connection;
ZQuery_rowcount.SQL.Add('select count(*) as num_rows from my_data');
ZQuery_rowcount.Active := True;
repeat
try
ZQuery_rowcount.Refresh;
except
on E:Exception do
begin
FCS.Enter;
try
FErrMsg := 'Refresh: [' + E.Message + ']';
finally
FCS.Leave;
end;
end;
end;
FCS.Enter;
try
try
FRowCount := ZQuery_rowcount.Fields[0].AsLargeInt;
except
on E:Exception do
FErrMsg := 'AsLargeInt: [' + E.Message + ']';
end;
finally
FCS.Leave;
end;
if not MessageLoop then
MsgWaitForMultipleObjects(1,
FSleepEvent,
false,
100,
QS_ALLINPUT);
until Terminated;
finally
ZQuery_rowcount.Free;
end;
finally
Connection.Free;
end;
except
on E:Exception do
begin
FCS.Enter;
try
FErrMsg := E.Message;
finally
FCS.Leave;
end;
end;
end;
end;