Objective c 强制从primative包装器一次一次访问指针

Objective c 强制从primative包装器一次一次访问指针,objective-c,multithreading,thread-safety,grand-central-dispatch,stdvector,Objective C,Multithreading,Thread Safety,Grand Central Dispatch,Stdvector,我已经阅读了大量关于线程安全的书籍,并且已经使用GCD将数学量大的代码从主线程中保留了一段时间(我在NSOperation之前就知道了这一点,而且它似乎仍然是更容易的选择)。然而,我想知道我是否可以改进目前使用锁的部分代码 我有一个ObjuleC++类,它是C++向量的包装器。(原因:原语浮动是在事先不知道限制的情况下不断添加的,容器必须是连续的,使用向量vs NSMutableData的原因是“正当原因”,这是我决定的,NSMutableData在调整自身大小时仍然会遇到相同的“过期”指针)

我已经阅读了大量关于线程安全的书籍,并且已经使用GCD将数学量大的代码从主线程中保留了一段时间(我在NSOperation之前就知道了这一点,而且它似乎仍然是更容易的选择)。然而,我想知道我是否可以改进目前使用锁的部分代码

我有一个ObjuleC++类,它是C++向量的包装器。(原因:原语浮动是在事先不知道限制的情况下不断添加的,容器必须是连续的,使用向量vs NSMutableData的原因是“正当原因”,这是我决定的,NSMutableData在调整自身大小时仍然会遇到相同的“过期”指针)

该类具有实例方法来添加经过处理并添加到向量(vector.push_back)的数据点。添加新数据后,我需要(通过不同的对象)对其进行分析。该处理发生在后台线程上,它使用直接指向向量的指针。目前,包装器有一个getter方法,该方法将首先锁定实例(它暂停本地串行队列的写入),然后返回指针。对于那些不知道的人来说,这样做是因为当向量用完空间时,推回会导致向量在内存中移动,为新条目腾出空间,从而使传递的指针无效。完成后,重数学的代码将在包装器上调用unlock,包装器将恢复排队写入完成

我看不到一种方法可以在不使用某种类型的锁或制作本地副本的情况下将指针传递一段未知的时间,这将是非常昂贵的

基本上:有没有更好的方法将基本指针传递给向量(或NSMutableData,对于那些被向量挂起的对象),即在使用指针时,对向量的任何添加都将排队,然后当指针的使用者完成时,自动“解锁”向量,并处理写入队列

当前实施情况

课程:

    代码>数据数组< /C> >:C++向量的包装器
  • DataProcessor
    :获取最原始的数据并在将其发送到“DataArray”之前进行清理
  • DataAnalyzer
    :获取“DataArray”指针并对数组进行分析
  • Worker
    :拥有并初始化所有3个,它还协调操作(它还做其他超出本文范围的事情)。它也是处理器和分析器的委托
发生了什么:

  • Worker
    正在侦听来自另一个处理外部设备的类的新数据
  • 当它收到带有数据包的
    NSNotification
    时,它通过
    -(void)checkNewData:(NSArray*)data将其传递到
    数据处理器
  • DataProcessor
    ,在后台线程中工作,清理数据(并保留部分数据),然后将
    DataArray
    告知
    -(void)addRawData:(float)data
    (如下所示)
  • DataArray
    然后存储该数据
  • DataProcessor
    使用当前块完成时,它会告诉
    Worker
  • 工作人员
    收到通知,处理完成后,它会通知
    数据分析器
    通过
    -(void)analyzeAvailableData开始处理新数据
  • DataAnalyzer
    做了一些准备工作,包括通过
    -(float*)dataPointer
    询问
    DataArray
    指针(如下所示)
  • DataAnalyzer
    向全局线程执行
    异步调度
    ,并开始重载。它需要始终访问数据指针
  • 完成后,它向主线程执行一个
    dispatch\u async
    ,以通知
    DataArray
    解锁数组
  • DataArray
    也可以由其他对象出于只读目的访问,但其他对象的读取速度非常快 代码从数据数组中剪切

    -(void)addRawData:(float)data {
        //quick sanity check
        dispatch_async(addDataQueue, ^{
            rawVector.push_back(data);
        });
    }
    
    - (float*)dataPointer {
        [self lock];
        return &rawVector[0];
    }
    
    - (void)lock {
        if (!locked) {
            locked = YES;
            dispatch_suspend(addDataQueue);
        }
    }
    
    - (void)unlock {
        if (locked) {
            dispatch_resume(addDataQueue);
            locked = NO;
        }
    }
    
    -(void)analyzeAvailableData {
        //do some prep work
    
        const float *rawArray = [self.dataArray dataPointer];
        dispatch_async(global_queue, ^{
            //lots of analysis
    
            //done
            dispatch_async(main_queue, ^{
                //tell `Worker` analysis is done
    
                [self.dataArray unlock];
            };
        };
    }
    
    数据分析器中截取代码

    -(void)addRawData:(float)data {
        //quick sanity check
        dispatch_async(addDataQueue, ^{
            rawVector.push_back(data);
        });
    }
    
    - (float*)dataPointer {
        [self lock];
        return &rawVector[0];
    }
    
    - (void)lock {
        if (!locked) {
            locked = YES;
            dispatch_suspend(addDataQueue);
        }
    }
    
    - (void)unlock {
        if (locked) {
            dispatch_resume(addDataQueue);
            locked = NO;
        }
    }
    
    -(void)analyzeAvailableData {
        //do some prep work
    
        const float *rawArray = [self.dataArray dataPointer];
        dispatch_async(global_queue, ^{
            //lots of analysis
    
            //done
            dispatch_async(main_queue, ^{
                //tell `Worker` analysis is done
    
                [self.dataArray unlock];
            };
        };
    }
    

    这实际上取决于您在做什么,大多数时候,我会使用
    dispatch\u async()
    dispatch\u sync()
    返回到主队列(或特定指定的队列)

    如果可以的话,异步显然更好

    这将取决于您的特定用例,但有时dispatch\u async/dispatch\u sync比创建锁快多个数量级

    grand central dispatch(和NSOperationQueue)的整个要点是消除传统线程编程中发现的许多瓶颈,包括锁


    关于您对
    NSOperation
    更难使用的评论。。。没错,我也不经常用。但它确实有一些有用的功能,例如,如果您需要在任务执行的中途或在任务开始执行之前终止任务,
    NSOperation
    是一种方法。

    有一种简单的方法可以在不锁定的情况下获得所需的内容。其思想是,您要么拥有共享的、不变的数据,要么拥有独占的、可变的数据。您不需要为共享的、不可变的数据设置锁的原因是,它只是只读的,因此在写入过程中不会出现争用情况

    您只需根据当前需要在两者之间切换:

    • 将样本添加到存储器时,需要以独占方式访问数据。如果您已经有了数据的“工作副本”,您可以根据需要对其进行扩展。如果只有对共享数据的引用,则创建一个工作副本,然后将其保留以供以后独占访问
    • 如果要评估样本,则需要对共享数据进行只读访问。如果你已经有一个共享副本,你只需要使用它。如果您只有独占访问工作副本,则可以转换
      dispatch_barrier_async(sync_queue, ^{
          _shared_value = 0;
          dispatch_async(mainQueue, ^{
              NSLog(@"value %d", _shared_value);
          });
      });