DispatchQueue障碍问题

时间:2017-10-05 06:35:48

标签: ios swift grand-central-dispatch

尝试创建线程安全数组,但它不像我预期的那样工作

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

public class SafeArray<Element> {
    private var array = [Element]()
    private let queue = DispatchQueue(label: "queueBarrier", attributes: .concurrent)

    public func append(element: Element) {
        queue.async(flags: .barrier) {
            self.array.append(element)
        }
    }

    public var elements: [Element] {
        var result = [Element]()
        queue.sync {
            result = self.array
        }
        return result
    }

    public var last: Element? {
        var result: Element?
        queue.sync {
            result = self.array.last
        }
        return result
    }
}




var safeArray = SafeArray<Int>()
var array = Array<Int>()

DispatchQueue.concurrentPerform(iterations: 10) { (int) in
    let last = array.last ?? 0
    array.append(last + 1)
    print("array = [..\(last)]")
}

print(array)

DispatchQueue.concurrentPerform(iterations: 10) { (int) in
    let last = safeArray.last ?? 0
    safeArray.append(element: last + 1)
       print("safeArray = [..\(last)]")
}

print(safeArray.elements)

output of my code

我预计数组应该有一些混乱,但safeArray应该有0到9之间的数字。

我了解数组有3个值,但 safeArray 有10个值符合预期。但为什么这个值不是从0到9?

谢谢!

4 个答案:

答案 0 :(得分:1)

barrier的工作方式是确保DispatchWorkItemappend(:_)的块)在执行其{{ 1}}(块中的代码)。 因此,DispatchWorkItem

如果仔细观察,您的perform通话中就会有一个(barrier)。 由于您在DispatchWorkItem中同时同时呼叫last,因此队列中将有last个堆栈在等待中。

这意味着您的所有DispatchQueue.concurrentPerform调用都将处于等待状态,因为它们被标记为DispatchWorkItem,并且您的append(_:)调用都将首先执行,因此得到很多零,直到所有{ barrier的{​​1}}个步骤是在挤入两个last

之前完成的

DispatchWorkItem在并发队列中工作的方式是,它实际上要等到所有待处理的last完成之后才能开始,并且在完成之前,没有其他并发操作会同时开始。 除非我弄错了,否则它会暂时“禁用”队列的并发性质。

我通常不愿介绍其他人在这里提出的锁或信号灯,除非您先了解GCD的工作原理,否则它们可能会引起更多问题。

您似乎正在尝试解决两件事,首先是让appends(_:)同时工作,然后根据其当前状态在并行操作中对数组进行变异。尝试先分解您要解决的问题,以便有人可以为您提供更好的答案。

答案 1 :(得分:0)

我已经创建了一个线程安全的NSMutableArray并按预期工作。

    //
//  GCDTSNSMutableArray.m
//  GCD
//
//  Created by Vikas Kumar Jangir on 07/05/19.
//  
//

#import "GCDTSNSMutableArray.h"

@interface GCDTSNSMutableArray()
@property (nonatomic,strong) NSMutableArray *internalArray;
@property (nonatomic) dispatch_queue_t queue;
@property (nonatomic, strong) NSString *queueName;
@end


@implementation GCDTSNSMutableArray

- (instancetype)init {
    self = [super init];
    if (self) {
        self.internalArray = [NSMutableArray new];
        //Make unique queue for every new insatance.
        self.queueName = [NSString stringWithFormat:@"GCDTSNSMutableArray_%@",[GCDCommonUtil generateUUID]];
        self.queue = dispatch_queue_create([self.queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

#pragma mark -  Add operations

- (void)addObject:(id)object {
    // Check for valid input object
    if (object == nil) {
        NSLog(@"Object must be nonnull");
        return;
    }

    // Check for valid input index
    dispatch_sync(self.queue, ^{
        [self.internalArray addObject:object];
    });
}

- (void)insertObject:(id)object atIndex:(NSUInteger)index {

    // Check for valid input object
    if (object == nil) {
        NSLog(@"Object must be nonnull");
        return;
    }

    // Check for valid input index
    NSUInteger numberOfElements = [self count];
    if (index > numberOfElements) {
        NSLog(@"Index %lu is out of range [0..%lu]",(unsigned long)index,(unsigned long)numberOfElements);
        return;
    }

    dispatch_sync(self.queue, ^{
        [self.internalArray insertObject:object atIndex:index];
    });
}

- (void)addObjectsFromArray:(NSArray *)array {
    // Valid input array
    if (array == nil) {
        NSLog(@"Array must be nonnull");
        return;
    }

    if ([array count] == 0) {
        NSLog(@"Array must be not empty");
        return;
    }

    // Add objects from array
    dispatch_sync(self.queue, ^{
        [self.internalArray addObjectsFromArray:array];
    });
}



#pragma mark - Remove Operation

- (void)removeObject:(NSObject *)object {
    // Valid input object
    if (object == nil) {
        NSLog(@"Object must be nonnull");
        return;
    }

    // Remove object from array
    dispatch_sync(self.queue, ^{
        [self.internalArray removeObject:object];
    });
}

- (void)removeObjectAtIndex:(NSUInteger)index {
    // Valid input index
    NSUInteger numberOfElements = [self count];
    if (index >= numberOfElements) {
        NSLog(@"Index is out of range");
        return;
    }

    // Remove object at index from array
    dispatch_sync(self.queue, ^{
        [self.internalArray removeObjectAtIndex:index];
    });
}

- (void)removeLastObject {
    dispatch_sync(self.queue, ^{
        [self.internalArray removeLastObject];
    });
}


- (void)removeAllObjects {
    // Check nonempty array
    NSUInteger numberOfElements = [self count];
    if (numberOfElements == 0) {
        NSLog(@"Array is empty");
        return;
    }

    // Remove all objects from array
    dispatch_sync(self.queue, ^{
        [self.internalArray removeAllObjects];
    });
}


#pragma mark - Count,Search,Copy

-(NSUInteger)count {
    __block NSUInteger count = 0;
    dispatch_sync(self.queue, ^{
        count = [self.internalArray count];
    });
    return count;
}

- (id)copy {
    __block id returnArray;
    dispatch_sync(self.queue, ^{
        returnArray = [self.internalArray copy];
    });

    return returnArray;
}

- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
    dispatch_sync(self.queue, ^{
        [self.internalArray replaceObjectAtIndex:index
                                      withObject:anObject];
    });
}

- (id)objectAtIndex:(NSUInteger)index {
    // Valid input index
    NSUInteger numberOfElements = [self count];
    if (index >= numberOfElements) {
        NSLog(@"Index %lu is out of range [0..%lu]",(unsigned long)index,(unsigned long)numberOfElements);
        return nil;
    }

    // Return object at index in array
    id __block object;
    dispatch_sync(self.queue, ^{
        object = [self.internalArray objectAtIndex:index];
    });
    return object;
}

- (NSUInteger)indexOfObject: (NSObject *)object {
    NSUInteger __block result;
    dispatch_sync(self.queue, ^{
        result = [self.internalArray indexOfObject:object];
    });
    return result;
}

- (BOOL)containsObject: (id)object {
    BOOL __block result;
    dispatch_sync(self.queue, ^{
        result = [self.internalArray containsObject:object];
    });
    return result;
}

- (NSArray *)toNSArray {
    NSArray __block *array;
    dispatch_sync(self.queue, ^{
        array = [[NSArray alloc] initWithArray:self.internalArray];
    });
    return array;
}

- (void)enumerateObjectsUsingBlockInSync:(BOOL)sync withBlock:(__attribute__((noescape)) void (^)(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop))block {

    if (!sync) {
        [self.internalArray enumerateObjectsUsingBlock:block];
    } else {
        dispatch_sync(self.queue, ^{
            [self.internalArray enumerateObjectsUsingBlock:block];
        });
    }
}


- (void)executeOnSynchWithCompletionBlock:(GCDThreadSafeNSMutableArrayCompletionBlock)compBlock {
    dispatch_sync(self.queue, compBlock);
}

@end

答案 2 :(得分:-1)

为什么不使用DispatchSemaphore

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

public class SafeArray<Element> {
    private var array = [Element]()
    private let semaphore = DispatchSemaphore(value: 1)
    private var lastEl: Element?

    public func append(element: Element) {

            self.array.append(element)
    }

    public var elements: [Element] {
        var result = [Element]()
        result = self.array
        return result
    }

    public var last: Element? {
        self.semaphore.wait()
        lastEl = self.array.last
        self.semaphore.signal()
        return lastEl
    }
}




var safeArray = SafeArray<Int>()
var array = Array<Int>()

DispatchQueue.concurrentPerform(iterations: 10) { (int) in
    let last = array.last ?? 0
    array.append(last + 1)
    print("array = [..\(last)]")
}

print(array)

DispatchQueue.concurrentPerform(iterations: 10) { (int) in
    let last = safeArray.last ?? 0
    safeArray.append(element: last + 1)
    print("safeArray = [..\(last)]")
}

print(safeArray.elements)

答案 3 :(得分:-1)

否你不会得到价值形式1 - 10 !!!,因为DispatchQueue.concurrentPerform同时执行多项操作

让我解释一下

假设DispatchQueue.concurrentPerform将同时执行3个项目,然后在这里

let last = safeArray.last ?? 0

你会得到0三倍但是它通过添加1来附加数组

您的数组变得像[1,1,1 ...]

在输出中没有barrie的作用,我还在清除

  

当屏障进程正在运行时,仍然会阻止读者。即使有几个读取器块已经并行运行,屏障过程也会等待所有读者完成开始写入之前

如果您需要阻止行为,则应使用DispatchSemaphore MjZac 解释

希望你能清楚