我有一个使用 DSPACK 组件库用 Delphi 6 编写的 DirectShow 推送源过滤器。我正在为推送源过滤器的 FillBuffer() 调用实施阻塞策略。推送源过滤器将音频数据接收到存储在缓冲区集合中的一个或多个缓冲区中。缓冲区集合中的每个方法都受临界区保护。
集合中的每个缓冲区都由不同的源提供数据,并且每个源都有自己的线程。FillBuffer() 调用会阻塞,直到集合中的每个缓冲区都有足够的数据来满足 FillBuffer() 被要求提供的数据量,因为数据混合在一起形成一个合并的缓冲区,然后返回给调用者。
这是我为避免竞争条件和死锁而设计的策略。缓冲存储集合有一个称为isEnoughData()的方法,它执行此操作:
- 获取保护集合中所有其他方法的相同关键部分。
- 迭代每个缓冲区检查以查看每个缓冲区是否有足够的数据来满足当前请求。
- 如果有足够的数据,则返回 TRUE
- 如果没有足够的数据,则获取一个用于方便阻塞的Mutex,返回 FALSE,并将请求的无法填充的字节数存储在 FNumPendingBytesRequested 中。
- 在返回之前,它当然会释放关键部分。
FillBuffer() 执行以下操作:
- 调用 isEnoughData()
- 如果返回 TRUE,它将混合(合并的缓冲区)数据返回给提供的Sample中的调用者。
- 如果为 FALSE,它会在上面概述的isEnoughData()调用期间由集合获取的Mutex上执行 WaitForSingleObject() ,从而阻塞直到出现足够的数据。当它获取Mutex时,它从集合中获取数据并将其返回给调用者,然后释放Mutex。
存储集合中将数据添加到集合中任何缓冲区的所有方法在返回之前检查是否现在可以满足 FNumPendingBytesRequested。如果是这样,则释放互斥锁,从而解除对 FillBuffer() 调用的阻塞,然后获取数据并将其返回。自然,该集合在销毁时会释放Mutex 。
这对我来说似乎很防弹。我相信它可以充分保护对 isEnoughData() 的 FillBuffer() 调用和向集合添加数据的其他线程之间的竞争条件。我也看不到任何可能发生竞争条件或死锁的地方。我对此是否正确?任何有关该策略的提示或警告也值得赞赏。
最大的问题:我能看到的唯一潜在问题是,在 FillBuffer() 有机会调用WaitForSingleObject()并阻塞之前,另一个线程是否添加了足够的数据来释放Mutex 。但我的理解是,在不属于任何人的同步对象上调用WaitForSingleObject()会立即返回对象的所有权,因此这应该不是问题,因为这意味着现在可以使用必要的数据。我的理解正确吗?