在 OperationQueue 中设置背压(或替代 API,例如 PromiseKit、Combine Framework)

Setting backpressure in OperationQueue (or alternative API, e.g. PromiseKit, Combine Framework)

我在处理许多图像的管道中有 2 个步骤:

如果没有背压,第 1 步的所有工作可能会堆积起来,导致仅用于打开图像的高内存使用率。

我想我可以使用一个信号量(例如 5),它大致表示我愿意为步骤 1(5 张图片)提供的内存量。我想这会使我的 5 个后台线程阻塞,这可能是一件坏事? (这是一个严肃的问题:阻塞后台线程是不是很糟糕,因为它会消耗资源。)

如果您使用的是 Combine,flatMap 可以提供背压。 FlatMap 为它收到的每个值创建一个发布者,但当它达到指定的最大未完成发布者数量时施加背压。

这是一个简化的例子。假设你有以下功能:

func loadImage(url: URL) -> AnyPublisher<UIImage, Error> {
   // ...
}

func doImageProcessing(image: UIImage) -> AnyPublisher<Void, Error> {
   // ...
}
let urls: [URL] = [...] // many image URLs

let processing = urls.publisher
    .flatMap(maxPublishers: .max(5)) { url in 
        loadImage(url: url)
           .flatMap { uiImage in
              doImageProcessing(image: uiImage)
           }
    }

在上面的示例中,它将加载 5 张图像,并开始处理它们。第 6 张图像将在前面的图像之一完成处理后开始加载。

如果您真的想使用 OperationQueue,只需将队列的 maxConcurrentOperationCount 设置为 5 即可防止同时启动超过 5 个操作。