2015-10-15 37 views
2

我有一个嵌套对象的对象。对象机构组织的简图将是:如何保护和访问多线程应用程序中的嵌套对象

TMainObj 
-TState 
-TDescriptor 
-List<TSubObj> 
--TSubObj_n 
---TSubObjState 
---TSubObjDesc 
---TSubObjData 

type TState = class 
end; 

type TDesc = class 
end; 

type TSubObjState = class 
end; 

type TSubObjDesc = class 
end; 

type TSubObjData = class 
end; 

type TSubObj = class 
    FSubObjState: TSubObjState; 
    FSubObjDesc: TSubObjDesc; 
    FSubObjData: TSubObjData; 
end; 

type TListSubObj = class (TList<TSubObj>) 
end; 

type TMainObj = class 
    FState: TState; 
    FDesc: TDesc; 
    FList: TList<TSubObj> 
end; 

我的多线程应用程序,我必须能够访问对象及其属性(其不包括在本实施例中的代码)。有些线程共享相同的对象,但仍然可以与主线程共享某些属性,因此我需要保护数据。我正在用关键部分/互斥体保护数据。不过,我不知道如何在这个方案中组织锁定机制以充分利用它。

我最初的想法是在TMainObj上实现锁定/解锁,并且每当任何线程需要访问任何属性或子对象时,它将锁定完整的TMainObj,并且所有其他线程将需要等待,直到TMainObj变为解锁。出于这个原因,我认为这不是一个好主意。一些线程不需要访问TMainObj的属性,但只需要它的子对象,如TState。我认为没有必要锁定整个TMainObj,但只有TState或我错过了什么?

如果我需要访问TMainObj性能我会做:

TMainObj.Lock 
try 
    TMainObj.Name := 'Just name!'; 
    TManiObj.Id := 1; 
finally 
    TMainObj.Unlock; 
end; 

和所有其他线程必须等待获得的访问。

但是什么时候我需要访问子类TDescriptor?我可以这样做:

TMainObj.Lock 
try 
    TMainObj.Descriptor.DataLen := 1024; 
    TManiObj.Descriptor.Count := 10; 
finally 
    TMainObj.Unlock; 
end; 

并且完整的TMainObj将被锁定。所有其他线程即使对“更改TMainObj的属性不感兴趣”也需要等待。

还是那个样子只锁定子对象描述:

Thread1: 
    TMainObj.Descriptor.Lock 
    try 
     TMainObj.Descriptor.DataLen := 1024; 
     TManiObj.Descriptor.Count := 10; 
    finally 
     TMainObj.Descriptor.Unlock; 
    end; 

同时一些其他线程仍然可以访问TMainObj性质和改变他们,对不对?

Thread2: 
    TMainObj.Lock; 
    try 
     TMainObj.Name := 'New name!'; 
    finally 
     TMainObj.Unlock; 
    end; 

这是显示每个线程访问的方式和内容的图像。 Threads accessing one object with subobjects

其中一个问题是死锁情况。在下一个例子中,我想展示不同的线程如何访问MainObj的不同“部分”。

MainThread:

MainObj.Lock; 
try 
    MainObj.Name = 'Different name!' 
    MainObj.Id = 2; 
finally 
MainObj.Unlock; 
end; 

同时线程1是这样做的:

MainObj.Descriptor.Lock; 
try 
    MainObj.Descriptor.DataLen = 1024; 
    MainObj.Descriptor.Count = 1; 
finally 
    MainObj.Descriptor.Unlock; 
end; 

所以两者的共享MainObj但每次改变对象的自己的一部分。这种锁定方式是否合适?

我希望我尽可能清楚地解释我的问题。我的问题是如何保护从不同线程访问这种对象结构?我是否必须使用自己的锁定/解锁对方法(和关键部分)来保护每个子对象?

+0

MainThread正在与TMainObject和它的属性和子对象(TSTATE,TDescription)。我有其他线程。一个只使用TState,另一个只使用TDescription。所以每个线程都与mainthread共享一些东西。因此,当主线程仅更改MainObject上的属性时,锁必须仅锁定那些属性,而其他线程仍可以更改TState或TDescriptor对象。我的问题是,如果这是确定的,我只锁定对象的某些部分,或者我应该锁定完整的对象? – Nix

+1

很难建议你没有完整的图片,在我看来 –

+0

请为你的Delphi版本添加一个标签 - 可用的线程代码有所不同。 –

回答

1

您可以使用TMonitor来为此添加任何东西到您的对象。在这种情况下,你的代码应该是这样的:

TMonitor.Enter(MainObj.Descriptor); 
try 
    MainObj.Descriptor.DataLen := 1024; 
    MainObj.Descriptor.Count := 10; 
finally 
    TMonitor.Exit(MainObj.Descriptor); 
end; 

只要试图访问描述所有线程(与主线程)做同样的事情那么他们将锁定等待下一个线程来完成。

你需要注意死锁,但是从你说的话不应该是一个问题。如果你做这样的事情会发生死锁:

Main Thread 
    Lock MainObj 
    Lock MainObj.Descriptor (waits for thread 1) 

如果线程1走来,并做到这一点:

Thread 1 
    Lock MainObj.Descriptor 
    Lock MainObj (waits for main thread) 
+0

这正是我关心的问题。有嵌套对象时的死锁情况。首先,我需要弄清楚在我的情况下是否会出现可能导致这种僵局的情况。我读过Eric Grange比较TCriticalSection和TMonitor的帖子,他的结论是,如果性能很重要,他的结论是不使用TMonitor,所以我认为使用TCriticalSection是更好的选择。 – Nix

+1

在性能方面对TMonitor进行了一些改进。阅读Eric博客中的评论。 – Graymatter

+1

在Windows上,我认为你不得不疯狂地使用本地进程锁定 - 关键部分。 TMonitor的记录非常糟糕。 –