ios DispatchQueue障碍问题

rn0zuynd  于 2023-02-01  发布在  iOS
关注(0)|答案(4)|浏览(128)

我试图使线程安全数组,但它的工作不是我所期望的

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)

我预计数组应该有一些混乱,但safeArray应该有从0到9的数字。
我知道array有3个值,而safeArray有10个值,但为什么这些值不是从0到9?
谢谢大家!

o2gm4chl

o2gm4chl1#

barrier的工作方式是确保DispatchWorkItemappend(:_)的代码块)在执行其perform(代码块内的代码)之前等待,直到所有其他DispatchWorkItem完成。
如果你仔细观察,你会发现在last调用中有一个(DispatchWorkItem),因为你调用last是你在DispatchQueue.concurrentPerform并发做的第一件事,你会有一堆DispatchWorkItem在队列中等待。
这意味着您的所有append(_:)调用都将等待,因为它们被标记为barrier,并且您的last调用都将首先执行,因此会得到很多零,直到last的所有DispatchWorkItem都完成,然后再压缩两个appends(_:)
barrier在并发队列中的工作方式是,它实际上会等到所有挂起的DispatchWorkItem都完成后才启动,并且在它完成之前,不会有任何其他东西与它并发启动。除非我弄错了,否则它可以暂时“禁用”队列的并发特性。
我通常不太愿意像其他人建议的那样引入锁或信号量,除非您首先了解GCD的工作原理,否则它们可能会导致更多问题。
看起来你要解决两个问题,首先是一个append(_:)可以并发工作,然后根据数组的当前状态在并行操作中改变数组。尝试先分解你要解决的问题,这样别人就可以给予你更好的答案。

e3bfsja2

e3bfsja22#

为什么不使用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)
4xy9mtcn

4xy9mtcn3#

请记住,虽然barrier使操作本身成为线程安全的,但两个操作之间的顺序是未定义的。因此,如果你想依赖于self.array.last的值,你必须在 * 你能够通过barrier之后 * 才能得到它的值。也就是说,你可以有一个如下的函数:

public func appendAfterOperationOnLast(_ operation: @escaping (Element?) -> Element) {
        queue.async(flags: .barrier) {
            let last = self.array.last // <- get the value here
            let element = operation(last)
            self.array.append(element)
        }
    }

然后

DispatchQueue.concurrentPerform(iterations: 10) { i in
    safeArray.appendAfterOperationOnLast { last in
        return (last ?? 0) + 1
    }
}

print(safeArray.elements)

打印预期结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
kx1ctssn

kx1ctssn4#

我已经创建了一个线程安全的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

相关问题