gpt4 book ai didi

delphi - 将读锁升级为写锁时 TMultiReadExclusiveWriteSynchronizer 的行为

转载 作者:行者123 更新时间:2023-12-03 15:05:33 25 4
gpt4 key购买 nike

如何实现这样的同步结构:

Lock.BeginRead
try
if Changed then
begin
Lock.BeginWrite;
try
Update;
finally
Lock.EndWrite;
end;
// ... do some other stuff ...
end;
finally
Lock.EndRead;
end;

在 EndWrite 之后不会丢失读锁,以便在执行此代码块时没有其他编写者可以执行。

在这种情况下,Delphi 2009 的 TMuliReadExclusiveWriteSynchronizer 的行为如何?

最佳答案

这个问题似乎包含两个标准:

  • “EndWrite 后不会丢失读锁”
  • “执行此代码块时,其他编写器无法执行”

我不会进一步讨论第一点,因为其他人已经这样做了。然而第二点非常微妙,需要解释。

首先,我指的是 Delphi 2007。我无法访问 2009。但是,我描述的行为不太可能发生改变。

您显示的代码确实使其他编写者可以在代码块期间更改值。当读锁升级为写锁时,读锁会暂时丢失。有时您的线程既没有读锁也没有写锁。这是设计使然,否则几乎肯定会出现死锁。 如果正在将读锁提升为写锁的线程在执行此操作时实际上持有读锁,则很容易发生以下情况:

  1. (线程1)获取读锁
  2. (线程2)获取读锁(好的,读锁是共享的)
  3. (线程 1)获取写锁(阻塞;线程 2 拥有读锁)
  4. (线程 2)获取写锁(阻塞;线程 1 有读锁——现在死锁)

为了防止这种情况发生,TMuliReadExclusiveWriteSynchronizer 在获取写锁之前会“瞬间”释放读锁。

(旁注:EDN 上的文章 Working with TMultiReadExclusiveWriteSynchronizer,在“锁定克里斯,我即将...”部分中似乎错误地表明我刚才提到的场景实际上会陷入僵局。这可能会已经写过有关 Delphi 早期版本的文章,或者它可能只是错误的。或者我可能误解了它的主张。但是请查看文章中的一些评论。)

因此,在不对上下文进行更多假设的情况下,您显示的代码几乎肯定是不正确的。在拥有读锁的情况下检查某个值,然后将其提升为写锁并假设该值未更改,这是一个错误。这是 TMuliReadExclusiveWriteSynchronizer 的一个非常微妙的捕获。

以下是 Delphi 库代码中注释的一些精选部分:

Other threads have an opportunity to modify the protected resource when you call BeginWrite before you are granted the write lock, even if you already have a read lock open. Best policy is not to retain any info about the protected resource (such as count or size) across a write lock. Always reacquire samples of the protected resource after acquiring or releasing a write lock. The function result of BeginWrite indicates whether another thread got the write lock while the current thread was waiting for the write lock. Return value of True means that the write lock was acquired without any intervening modifications by other threads. Return value of False means another thread got the write lock while you were waiting, so the resource protected by the MREWS object should be considered modified. Any samples of the protected resource should be discarded. In general, it's better to just always reacquire samples of the protected resource after obtaining a write lock. The boolean result of BeginWrite and the RevisionLevel property help cases where reacquiring the samples is computationally expensive or time consuming.

这里有一些可以尝试的代码。创建一个名为 Lock 的全局 TMultiReadExclusiveWriteSynchronizer。创建两个全局 bool 值:Bad 和 GlobalB。然后启动每个线程的一个实例,并从主程序线程监视 Bad 的值。

type
TToggleThread = class(TThread)
protected
procedure Execute; override;
end;

TTestThread = class(TThread)
protected
procedure Execute; override;
end;

{ TToggleThread }

procedure TToggleThread.Execute;
begin
while not Terminated do
begin
Lock.BeginWrite;
try
GlobalB := not GlobalB;
finally
Lock.EndWrite;
end;
end;
end;

{ TTestThread }

procedure TTestThread.Execute;
begin
while not Terminated do
begin
Lock.BeginRead;
try
if GlobalB then
begin
Lock.BeginWrite;
try
if not GlobalB then
begin
Bad := True;
Break;
end;
finally
Lock.EndWrite;
end;
end;
finally
Lock.EndRead;
end;
end;
end;

虽然它是不确定的,但您可能会很快(不到 1 秒)看到值 Bad 被设置为 True。所以基本上你看到 GlobalB 的值是 True,然后当你第二次检查它时它是 False,即使两次检查都发生在 BeginRead/EndRead 对之间(原因是因为里面还有一个 BeginWrite/EndWrite 对) .

我个人的建议:永远不要将读锁升级为写锁。这太容易出错了。无论如何,您永远不会真正将读锁提升为写锁(因为您暂时失去了读锁),因此您也可以通过在 BeginWrite 之前调用 EndRead 来在代码中显式地实现这一点。是的,这意味着您必须再次检查 BeginWrite 内的条件。因此,在您最初展示的代码的情况下,我什至根本不需要读锁。只需从 BeginWrite 开始,因为它可能决定写入。

关于delphi - 将读锁升级为写锁时 TMultiReadExclusiveWriteSynchronizer 的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1187382/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com