multithreading - 如何在多线程应用程序中保护和访问嵌套对象

标签 multithreading delphi critical-section delphi-xe8

我有一个带有嵌套对象的对象。对象组织的简单图为:

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;

我有多线程应用程序,必须启用对对象及其属性的访问(本示例代码中未包含)。有些线程共享一些相同的对象,有些却没有,但是在我需要保护数据之前,它们仍然可以与主线程共享某些属性。我正在使用关键部分/互斥来保护数据。但是,我不知道如何在此方案中组织锁定机制以从中获得最大的 yield 。

我最初的想法是在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的不同“部分”。

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

同时thread1正在这样做:
MainObj.Descriptor.Lock;
try
   MainObj.Descriptor.DataLen = 1024;
   MainObj.Descriptor.Count   = 1;
finally
  MainObj.Descriptor.Unlock;
end;

因此,两者都共享MainObj,但每个对象都在更改自己的对象部分。这种锁定方法合适吗?

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

最佳答案

您可以为此使用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)

关于multithreading - 如何在多线程应用程序中保护和访问嵌套对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33154902/

相关文章:

delphi - 为什么不使用常量时没有警告或提示?

windows-vista - Vista/Win2008 上的临界区会泄漏内存吗?

c - 在什么情况下 Windows 临界区可能有一个负的锁定计数?

delphi - 写保护 USB 驱动器上出现 ShellExecuteEx 错误?

synchronization - OpenMP 命名的关键部分 : if a program variable is used, 是被评估还是被用作字符串而不被评估?

java - JVM 性能、线程负载和分析,以优化基于 Java 的项目的性能

c# - 这个静态方法是线程安全的吗?

java - java线程异常

java - 当线程中断时继续 hibernate 剩余时间

Delphi - 应用程序的 .bpl 插件 - 加载 > 1 个插件时出现问题