DispatchQueue 障碍问题
DispatchQueue barrier issue
正在尝试创建线程安全数组,但效果不如我预期
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?
谢谢!
为什么不使用 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)
barrier
的工作方式是确保 DispatchWorkItem
(append(:_)
的块)在执行其之前等待所有其他 DispatchWorkItem
完成perform
(块内的代码)。
因此,barrier
。
如果你仔细观察,你的 last
调用中有一个 (DispatchWorkItem
)。
由于您在 DispatchQueue.concurrentPerform
中 同时 做的第一件事是调用 last
,您将有一堆 DispatchWorkItem
在队列中等待.
这意味着您所有的 append(_:)
调用都将等待,因为它们被标记为 barrier
并且您的 last
调用将全部首先执行,因此会得到很多零直到所有last
的 DispatchWorkItem
是在压缩成两个 appends(_:)
之前完成的
barrier
在并发队列中的工作方式是,它实际上会等到所有待处理的 DispatchWorkItem
完成后才开始,并且在它完成之前不会同时开始其他任何事情.
它暂时 "disables" 队列的并发性质,除非我弄错了。
我通常不太愿意按照其他人的建议引入锁或信号量,除非您首先了解 GCD 的工作原理,否则它们可能会导致更多问题。
看起来您正在尝试解决两件事,首先是让 append(_:)
并发工作,然后根据其当前状态在并行操作中改变数组。尝试首先分解您要解决的问题,以便有人可以给您更好的答案。
我创建了一个 thread-safe 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
正在尝试创建线程安全数组,但效果不如我预期
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?
谢谢!
为什么不使用 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)
barrier
的工作方式是确保 DispatchWorkItem
(append(:_)
的块)在执行其之前等待所有其他 DispatchWorkItem
完成perform
(块内的代码)。
因此,barrier
。
如果你仔细观察,你的 last
调用中有一个 (DispatchWorkItem
)。
由于您在 DispatchQueue.concurrentPerform
中 同时 做的第一件事是调用 last
,您将有一堆 DispatchWorkItem
在队列中等待.
这意味着您所有的 append(_:)
调用都将等待,因为它们被标记为 barrier
并且您的 last
调用将全部首先执行,因此会得到很多零直到所有last
的 DispatchWorkItem
是在压缩成两个 appends(_:)
barrier
在并发队列中的工作方式是,它实际上会等到所有待处理的 DispatchWorkItem
完成后才开始,并且在它完成之前不会同时开始其他任何事情.
它暂时 "disables" 队列的并发性质,除非我弄错了。
我通常不太愿意按照其他人的建议引入锁或信号量,除非您首先了解 GCD 的工作原理,否则它们可能会导致更多问题。
看起来您正在尝试解决两件事,首先是让 append(_:)
并发工作,然后根据其当前状态在并行操作中改变数组。尝试首先分解您要解决的问题,以便有人可以给您更好的答案。
我创建了一个 thread-safe 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