类实例“拥有”TMultiReadExclusiveWriteSynchronizer实例?

3
我是一名有用的助手,可以为您翻译文本。
我有一个多线程的Delphi程序,创建多个某些类的实例,我希望每个类实例都有自己的TMultiReadExclusiveWriteSynchronizer实例,以便在特定属性的get和set方法中使用。
例如,这是一个使用TMultiReadExclusiveWriteSynchronizer的类的部分单元:
interface

TSGThread=class(TThread)
private
    fWaiting:boolean;
    function getWaiting:boolean;
    procedure setWaiting(value:boolean);
public
    property waiting:boolean read getWaiting write setWaiting;
end;

implementation

var syncWaiting:TMultiReadExclusiveWriteSynchronizer;

function TSGThread.getWaiting:boolean;
begin
    syncWaiting.BeginRead;
    result:=fWaiting;
    syncWaiting.EndRead;
end;

procedure TSGThread.setWaiting(value:boolean);
begin
    syncWaiting.BeginWrite;
    fWaiting:=value;
    syncWaiting.EndWrite;
end;

initialization
    syncWaiting:=TMultiReadExclusiveWriteSynchronizer.Create;
finalization
    syncWaiting.Free;
end.

这样做的问题在于,该单元创建了一个TMultiReadExclusiveWriteSynchronizer实例,然后由多个TSGThread实例使用。
同步器仅控制TSGThread的私有字段访问。
线程A可以使用公共属性来修改线程B中的字段,因此需要同步器,但每个线程中应该有一个单独的同步器,以便这些线程不必等待对方,如果它们正在修改自己的属性。
Delphi帮助说“创建TMultiReadExclusiveWriteSynchronizer的全局实例”,但严格来说,它必须始终是全局的吗?
如果类仅保护其自己的属性访问,那么使用位于私有字段中的TMultiReadExclusiveWriteSynchronizer实例是否会起到同步作用?

在看到下面的答案后,我现在已经成功地使用私有字段中的同步器测试了我的类。 - Scott Leis
3个回答

4

这篇文章说:

创建与你想要保护的全局内存关联的TMultiReadExclusiveWriteSynchronizer的全局实例。

但是你没有全局内存。你有线程对象特定的内存,因此为每个线程对象创建同步对象。您可以根据需要创建尽可能多的同步对象。为您希望单独访问和保护的每个共享元素创建一个同步对象。


3

这可能有点离题,但是在你提供的例子中,同步器基本上是无用的。布尔值不能在线程上下文切换时部分读取/写入。

此外,以你的例子为例,像这样的代码

sgThread.Waiting := not sgThread.Waiting;

可能会失败。

现在回到主题。TMultiReadExclusiveWriteSynchronizer的作用域需要与其保护的资源一样大。由于您想保护对象的私有字段,因此可以将TMultiReadExclusiveWriteSynchronizer声明为私有字段。(您正在保护对私有字段的访问,而不是对属性的访问)


我使用同步器是因为最近我写了一些Java线程代码,读到了Java的“volatile”关键字,并且得出结论:每当不同的线程可以读/写相同的字段时,建议使用它。而Delphi的TMultiReadExclusiveWriteSynchronizer则是一个不错的等价物。 - Scott Leis
关于 sgThread.Waiting:=not sgThread.Waiting; 这种情况,属性的 set 方法被调用之前不会先计算右边表达式(完成 get 方法)吗? - Scott Leis
是的,在调用set方法之前,右侧将被评估。问题在于另一个线程实际上可以在Get和Set之间读取和修改该值,这可能会产生不可预测的结果。如果您有两个执行上述行的线程...假设每个线程执行10次。最终结果不总是相同的。 - Ken Bourassa

2

它不一定需要是全局变量,但为了保持线程安全,每次访问都必须通过同步器。一种简单的方法是将同步器设置为全局变量,但还有其他处理方式。


接受这个答案仅仅是因为它出现得更早,但是 Rob 的回答同样有用。 - Scott Leis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接