使队列线程安全



我有一个相机会话,我正在从缓冲区拍摄图像:

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];        
//rotate image 90°
ciImage = [ciImage imageByApplyingTransform:CGAffineTransformMakeRotation(-M_PI/2.0)];
}

我在图像上应用了一个过滤器,我希望它在另一个队列上应用过滤器,这是线程不安全的,所以如果我快速拍摄图像,它会将图像混合在一起(我认为从左到右50/50),但我正在努力使其线程安全,使用NSLock或NSRecursiveLock将无法工作,因为它会将图片混合在一起。

dispatch_async(filterQueue, ^{
CIImage *scaleImage = [CIFilter filterWithName:@"CILanczosScaleTransform" keysAndValues:kCIInputImageKey, ciImage, @"inputScale", [NSNumber numberWithFloat:0.5], nil].outputImage;
CGImageRef cgImage = [imageContext createCGImage:scaleImage fromRect:scaleImage.extent];
[self.pictureArray addObject:[UIImage imageWithCGImage:cgImage]];
CGImageRelease(cgImage);
});

有人能帮我吗?我不知道如何使代码成为线程安全的

图像混合如下:https://i.stack.imgur.com/wjrIl.png

首先,上面代码中唯一不安全的部分是对addObject:的调用。您可以通过将addObject:调用移动到主线程,或者通过使pictureArray访问更加线程安全来确保代码线程的安全。让我们看看两者。

移动呼叫

这几乎可以肯定是你想要的方式。

dispatch_async(filterQueue, ^{
CIImage *scaleImage = [CIFilter filterWithName:@"CILanczosScaleTransform" keysAndValues:kCIInputImageKey, ciImage, @"inputScale", [NSNumber numberWithFloat:0.5], nil].outputImage;
CGImageRef cgImage = [imageContext createCGImage:scaleImage fromRect:scaleImage.extent];
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
cgImage = NULL; // Always set to NULL after you release something
dispatch_async(dispatch_get_main_queue(), ^{
[self.pictureArray addObject:image];
});
});

请注意,我正在尽可能多地处理后台线程。我只是将最后一个addObject:移到主线程。

线程安全addImage:

如果你经常从后台线程调用addObject:,那么把它提升到自己的方法中可能会很好,比如

- (void)addImageOnMainThread:(UIImage *)image {
dispatch_async(dispatch_get_main_queue(), ^{
[self.pictureArray addObject:image];
});
}

线程安全图片数组:

如果突变非常常见,并且锁定是一个性能问题,那么这种情况会更好。使用屏障会导致非常的快速访问,而代价是更复杂的代码。

- (UIImage *)imageAtIndex:(NSUInteger)index {
UIImage *result = nil;
dispatch_sync(self.pictureArrayQueue, 
^{ result = self.pictureArray[index]; });
return result;
}
- (void)addImage:(UIImage *)image {
dispatch_barrier_async(self.pictureArrayQueue, 
^{ [self.pictureArray addObject:image]; });
}

请注意,这里有一个新的调度队列用于访问pictureArray。您可以构建更多的读写器(如removeImageAtIndex:等)所有读写器都使用dispatch_sync。所有编写器都使用dispatch_barrier_async。这需要更多的代码,除非通过这些方法,否则永远不能直接访问pictureArray。优点是,如果突变很常见,它会更快。

使用@synchronized(self)使代码片段线程安全。

例如:

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)
//The following code will be thread safe.
@synchronized(self) {
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];        
//rotate image 90°
ciImage = [ciImage imageByApplyingTransform:CGAffineTransformMakeRotation(-M_PI/2.0)];
}
}

@synchronized语句将锁定这段代码,以便一次用于单个线程。您可以将它应用于任何需要单线程代码的地方。

希望能有所帮助。

最新更新