尝试创建线程安全数组,但它不像我预期的那样工作
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之间的数字。
我了解数组有3个值,但 safeArray 有10个值符合预期。但为什么这个值不是从0到9?
谢谢!
答案 0 :(得分:1)
barrier
的工作方式是确保DispatchWorkItem
(append(:_)
的块)在执行其{{ 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 解释
希望你能清楚