比赛条件 - 互斥体可以更灵活吗?
我有一个TThread对象,名为TTestThread。当创建线程时,它们会创建全局配置变量的本地副本。用户可以随时更改全局配置变量,并且当他这样做时,所有线程都会通过其本地变量UpdateLocalCopyOfConfigVariables通知变更。线程不直接依赖全局配置变量,因为它们可以在任何时候由用户修改,如果线程同时访问它们,将会创建竞争条件。比赛条件 - 互斥体可以更灵活吗?
这里的TTestThread:
type
TTestThread = class(TThread)
private
LocalConfigA : String;
LocalConfigB : Integer;
procedure UpdateLocalConfigIfNecessary;
protected
procedure Execute; override;
public
UpdateLocalCopyOfConfigVariables : Boolean;
constructor Create;
end;
implementation
constructor TTestThread.Create;
begin
inherited Create(false);
UpdateLocalCopyOfConfigVariables := true;
end;
procedure TTestThread.UpdateLocalConfigIfNecessary;
begin
WaitForSingleObject(ConfigurationLocker, INFINITE);
if (UpdateLocalCopyOfConfigVariables) then
begin
LocalConfigA := GlobalConfigA;
LocalConfigB := GlobalConfigB;
UpdateLocalCopyOfConfigVariables := false;
end;
ReleaseMutex(ConfigurationLocker);
end;
procedure TTestThread.Execute;
begin
while (not(Terminated)) do
begin
UpdateLocalConfigIfNecessary;
// Do stuff
end;
end;
正如你所看到的,我有一个适当的互斥体,以避免排序的前面所述的竞争条件。 WaitForSingleObject(ConfigurationLocker,INFINITE);当用户改变全局配置变量被称为:
procedure ChangeGlobalConfigVariables(const NewGlobalConfigA : String ; NewGlobalConfigB : Integer);
var
I : Integer;
begin
WaitForSingleObject(ConfigurationLocker, INFINITE);
GlobalConfigA := NewGlobalConfigA;
GlobalConfigB := NewGlobalConfigB;
for I := 0 to ThreadList.Count - 1 do
TTestThread(ThreadList[I]).UpdateLocalCopyOfConfigVariables := true;
ReleaseMutex(ConfigurationLocker);
end;
的事情是,虽然它可以防止线程在同一时间更新其本地配置变量的副本,全局配置变量被改变,这也防止两个线程同时更新其本地配置 - 即使全局配置变量未被更改。据我所知,在写作正在进行时,竞争条件是一个问题。如果全局配置变量相同,那么这些线程可以同时更新其本地副本而不会出现问题。
我对不对?如果是这样,有没有办法解决这个问题?当然,这不是一个大问题,但我仍然觉得必须有一个更好的解决方案......
只有在写入权限时才能锁定代码。MBo's answer显示了如何在写入锁定时允许多个线程读取变量。
但是,读取全局配置对象应该非常快。所以这不能成为这里的瓶颈。
如何更快处理?
1.使用TCriticalSection而非互斥
一个关键部分是不是互斥体快得多。互斥锁可用于多个进程,而关键部分仅适用于当前进程。你不需要这种昂贵的锁定。由此类似的变量ConfigurationCriticalSection: TCrticalSection
交换ConfigurationLocker
。您需要在启动时创建对象,因为它用作全局变量。
您以类似的方式使用临界区。例如:
procedure TTestThread.UpdateLocalConfigIfNecessary;
begin
ConfigurationCriticalSection.Enter;
try
if (UpdateLocalCopyOfConfigVariables) then
begin
LocalConfigA := GlobalConfigA;
LocalConfigB := GlobalConfigB;
UpdateLocalCopyOfConfigVariables := false;
end;
finally
ConfigurationCriticalSection.Leave;
end;
end;
try..finally..end
模式在这里非常重要。如果您不使用它,则在Enter
和Leave
之间发生异常时,最终可能会死锁。当您使用互斥体WaitForSingleObject
和ReleaseMutex
时,您需要应用相同的模式。
2。发送新值的副本到线程
不要让线程访问全局配置。 有许多不同的模式来通知其他对象的变化。一种方法是调用提供值的方法。这可能看起来像这样:
TTestThread(ThreadList[I]).UpdateLocalCopyOfConfigVariables(GlobalConfigA, GlobalConfigB);
因此,将调用TTestThread(ThreadList[I]).UpdateLocalCopyOfConfigVariables := true
将被替换。
线程对象将存储新值并将其应用于特定情况。
说有没有真正的,大规模的瓶颈时,你是对的。对于我来说,当他们实际上可以在没有问题的情况下实际读取全局配置变量时,让线程彼此等待,这让我觉得“奇怪”。虽然我很欣赏你的回答! –
读写器锁定比互斥或关键部分更合适。这样的锁可以允许多个线程同时从配置中读取而无需彼此等待,同时在需要更改配置时仍然提供排它锁。 –
只要注意'TMREWSync',它在某些情况下确实存在一些已知问题。如果您在Windows Vista +上运行,请考虑将Win32 [Slim Reader/Writer(SRW)Locks](https://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx)作为替代方案。 –
我们在谈论什么样的问题?我做了一个快速搜索,但我不确定你指的是什么。 –