从其他线程访问 VT 数据是否安全?
从辅助线程更改 VirtualTreeView 数据是否安全? 如果是,我应该使用关键部分(甚至同步方法)吗?
我担心当我从另一个线程写入 VT 的数据记录时,主线程同时调用其重绘,并且此刷新将导致同时读取同一记录。我想补充一下,我在应用程序中只使用了 2 个线程。
像...
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;
更新
我已将关键部分添加到代码中,从 TMyCreatedThread 类调用 GetNodeData 真的不安全吗,即使该函数仅返回指向记录的指针?
非常感谢
问候
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 GetNodeData from TMyCreatedThread class, even if this function only returns a pointer to the record ?
Thanks a lot
Regards
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
不,尤其是你这样做的方式。
VST
是一个视觉控件。MainForm
也是如此,您直接从线程中引用它。 GUI 控件不是线程安全的,不应直接从线程访问。此外,您还引用了线程中的全局变量“MainForm”。这绝对不是线程安全的。如果您需要从主窗体和单独的线程访问
VST
的数据,请勿将其直接存储在VST.Node.Data
中。将其保存在一个外部列表中,您可以用关键部分或其他一些线程安全方法包围该列表,并在线程中访问该外部列表(首先锁定它)或在VST
事件中访问主窗体。请参阅 Delphi RTL 中的TLockList
以获取可锁定列表的示例,并参阅TMultipleReadExclusiveWrite
以获取示例同步类。No, especially the way you're doing it.
VST
is a visual control. So isMainForm
, 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 inVST.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 theVST
events. SeeTLockList
in the Delphi RTL for an example of a lockable list, andTMultipleReadExclusiveWrite
for a sample synchronization class.