2015-10-15 35 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的記錄非常糟糕。 –