我有一个名为的方法render
,该方法在每一帧上都会被调用,以使用 Metal 在屏幕上绘制一些东西。它正在渲染存储在类中的一些点数组。但是,我偶尔需要更新这个点数组。我正在努力想出一个可以以并发安全的方式更新这些点的解决方案。
也就是说,当render
尝试访问此数组时,另一个线程可能正在更新它,从而导致崩溃。此外,render
是同步的,无法阻止或等待更新。这似乎是一个常见的并发问题,所以我在这里寻求帮助,看看有什么解决方案。
概括一下这个问题——有一些同步函数被非常频繁地调用,它需要访问可以由另一个线程更新的数据。我确实可以控制该render
方法和执行更新的“其他”线程。我怎样才能防止竞争条件导致应用程序崩溃?(它必须不崩溃,没有数据“正确性”的考虑)
我假设的一个可能的解决方案是使用原子,但我并不完全确定如何做到这一点,特别是在 Swift 中(不认为 Swift 有原子吗?)
编辑:
我将尝试在这里添加更多背景信息,但考虑到我正在实现的部分内容藏在我无法访问的私人存储库中,添加 MRE 可能会很困难。
简而言之,我正在尝试按照此示例实现 MapBox 自定义图层。示例中此特定函数的细节render
无关紧要,因为我的渲染方法需要执行完全不同的事情,但它在伪代码中看起来像这样:
class MyCustomLayer: NSObject, CustomLayerHost {
var locationData: [CLLocation] = []
func render(...) {
// calculate the viewport from the parameters
let viewport = ...
if viewport.fitSomeCondition {
// fetch new location data. This can't block and needs to happen async
fetchLocationDataFromServer(viewport: viewport)
// need to somehow update the locationData
}
// render locationData
}
}
这里locationData
是经度和纬度的数组,是MapBox iOS SDK协议的render
一部分。如何处理的源代码尚未公开,但这里是标题声明:CustomLayerHost
CustomLayerHost
/**
* Render the layer. This method is called once per frame.
*
* This method is called if underlying map rendering backend uses Metal graphic API.
*
* @param The `parameters` that define the current camera position.
* @param The MTLCommandBuffer used to render all map layers.
* Use to create new command encoders. Do not submit as there could be map
* layers following this custom layer in style's layer list and those won't get
* to be encoded.
* @param The MTLRenderPassDescriptor that defines rendering map to view drawable.
*
*/
- (void)render:(nonnull MBMCustomLayerRenderParameters *)parameters
mtlCommandBuffer:(nonnull id<MTLCommandBuffer>)mtlCommandBuffer
mtlRenderPassDescriptor:(nonnull MTLRenderPassDescriptor *)mtlRenderPassDescriptor;
我知道这不是 MRE,但我希望这有助于更清楚地解释这个问题。
如果你只是想防止数据竞争
locationData
,我建议添加一些同步。例如,在高性能场景中,我们可能倾向于基于锁的同步:有很多同步模式(GCD 串行队列、参与者等),但对于需要逐帧频繁运行的东西,我可能倾向于更高性能的同步机制,即不公平锁,如上所示。
注意,我们希望尽量减少同步的次数,因此,我会避免
locationData
在内重复引用render
,而是只引用一次,并将其分配给局部变量。另外,这超出了问题的范围,但请仔细考虑
fitSomeCondition
,并确保避免冗余或并发的网络请求。就其价值而言,我可能会考虑将这种基于锁的同步移动到它自己的属性包装器:
这简化了
MyCustomLayer
: