我有一个相机会话,我正在从缓冲区拍摄图像:
-(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);
});
有人能帮助我吗?我对如何使代码成为线程安全的知识不太了解
答案 0 :(得分:2)
首先,上述代码中唯一不是线程安全的部分是对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:
移动到主线程。
如果你经常从后台线程调用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
。优点是如果突变很常见,它会快得多。
答案 1 :(得分:0)
使用@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语句将锁定此段代码,以便一次用于单个线程。您可以将它应用于需要单线程代码的任何位置。
希望有所帮助。