Is it safe to change VirtualTreeView data from the secondary thread ? And if yes, should I use critical sections (or even Synchronize method) ?
I'm afraid that when I'll be writing to the VT's data record from the other thread, main thread invokes its repaint meanwhile and this refresh will cause reading of the same record at one time. I would supplement I'm using only 2 threads in the application.
Something like ...
type
PSomeRecord = ^TSomeRecord;
TSomeRecord = record
SomeString: string;
SomeInteger: integer;
SomeBoolean: boolean;
end;
...
var FCriticalSection: TRTLCriticalSection; // global for both classes
...
procedure TMyCreatedThread.WriteTheTreeData;
var CurrentData: PSomeRecord;
begin
EnterCriticalSection(FCriticalSection); // I want to protect only the record
CurrentData := MainForm.VST.GetNodeData(MainForm.VST.TopNode);
with CurrentData^ do // I know, the ^ is not necessary but I like it :)
begin
SomeString := 'Is this safe ? What if VT will want this data too ?';
SomeInteger := 777;
SomeBoolean := True;
end;
LeaveCriticalSection(FCriticalSection);
MainForm.VST.Invalidate;
end;
// at the same time in the main thread VT needs to get text from the same data
// is it safe to do it this way ?
procedure TMainForm.VST_GetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
var CellText: string);
var CurrentData: PSomeRecord;
begin
EnterCriticalSection(FCriticalSection); // I want to protect only the record
CurrentData := VST.GetNodeData(VST.TopNode);
with CurrentData^ do
begin
case Column of
0: CellText := SomeString;
1: CellText := IntToStr(SomeInteger);
2: CellText := BoolToStr(SomeBoolean);
end;
end;
LeaveCriticalSection(FCriticalSection);
end;
// I'm afraid the concurrent field reading may happen only here with the private VT fields
// FNodeDataSize, FRoot and FTotalInternalDataSize, since I have Node.Data locked by the
// critical sections in the VT events, some of those may be accessed when VT is refreshed
// somehow
function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer;
begin
Assert(FNodeDataSize > 0, 'NodeDataSize not initialized.');
if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
Result := nil
else
Result := PByte(@Node.Data) + FTotalInternalDataSize;
end;
Update
I've added the critical sections to the code, is it really unsafe to call GetNodeDa开发者_运维知识库ta from TMyCreatedThread class, even if this function only returns a pointer to the record ?
Thanks a lot
Regards
No, especially the way you're doing it.
VST
is a visual control. So is MainForm
, which you're referring to directly from your thread. GUI controls are not thread-safe, and shouldn't be accessed directly from a thread. In addition, you're referring to the global variable 'MainForm' from the thread. This is absolutely NOT thread-safe.
If you need to access the data for the VST
from both your main form and a separate thread, don't store it directly in VST.Node.Data
. Keep it in an external list that you can surround with a critical section or some other thread-safe method, and access that external list in the thread (locking it first) or your main form in the VST
events. See TLockList
in the Delphi RTL for an example of a lockable list, and TMultipleReadExclusiveWrite
for a sample synchronization class.
精彩评论