2

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

   -(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);
        });

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

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

4

2 回答 2

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];
  });
});

请注意,我在后台线程上尽我所能。我只是将finaladdObject:移到主线程。

线程安全的 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这需要更多代码,除非通过这些方法,否则您绝不能直接访问。优点是如果突变非常普遍,它会更快。

于 2012-12-10T14:56:36.530 回答
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 语句将锁定这部分代码以立即用于单个线程。您可以将它应用到任何需要单线程代码的地方。

希望有帮助。

于 2012-12-10T14:08:18.603 回答