Ios 以下是dispatch_set_target_queue()的安全用法吗?
我想做的是创建一个间接的 我的最终目标是将队列用作的属性。虽然使用了间接队列,但它在技术上遵循文档 之所以这样做,是因为对于异步操作来说并不安全,因为它是全局可访问的,并且设置为1。因此,你可以很容易地用这个操作队列击中自己的脚 更新1 关于这个问题假设的“异步NSO操作”的基础,似乎有很多混淆。需要明确的是,这是基于本文中的概念,特定的概念是使用“操作准备就绪”和依赖关系管理来管理应用程序中的任务,这意味着将异步NSOperations添加到NSOperationQueues以利用这一点。如果你把这些概念带到这个问题的精神上来,希望推理会更有意义,你可以专注于将解决方案与其他解决方案进行比较和对比 更新2-问题示例:Ios 以下是dispatch_set_target_queue()的安全用法吗?,ios,nsoperationqueue,foundation,libdispatch,dispatch-queue,Ios,Nsoperationqueue,Foundation,Libdispatch,Dispatch Queue,我想做的是创建一个间接的 我的最终目标是将队列用作的属性。虽然使用了间接队列,但它在技术上遵循文档 之所以这样做,是因为对于异步操作来说并不安全,因为它是全局可访问的,并且设置为1。因此,你可以很容易地用这个操作队列击中自己的脚 更新1 关于这个问题假设的“异步NSO操作”的基础,似乎有很多混淆。需要明确的是,这是基于本文中的概念,特定的概念是使用“操作准备就绪”和依赖关系管理来管理应用程序中的任务,这意味着将异步NSOperations添加到NSOperationQueues以利用这一点。如果
// VendorManager represents any class that you are not in direct control over.
@interface VendorManager : NSObject
@end
@implementation VendorManager
+ (void)doAnsyncVendorRoutine:(void (^)(void))completion {
// Need to do some expensive work, make sure we are off the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND 0), ^(void) {
// Some off main thread background work
sleep(10);
// We are done, go back to main thread
[NSOperationQueue.mainQueue addOperationWithBlock:completion];
});
}
@end
// MYAsyncBoilerPlateOperation represents all the boilerplate needed
// to implement a useful asnychronous NSOperation implementation.
@interface MYAlertOperation : MYAsyncBoilerPlateOperation
@end
@implementation MYAlertOperation
- (void)main {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:"Vendor"
message:"Should vendor do work?"
preferredStyle:UIAlertControllerStyleAlert];
__weak __typeof(self) weakSelf = self;
[alertController addAction:[UIAlertAction actionWithTitle:@"Yes"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[VendorManager doAnsyncVendorRoutine:^{
// implemented in MYAsyncBoilerPlateOperation
[weakSelf completeThisOperation];
}];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"No"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakSelf cancel];
}]];
[MYAlertManager sharedInstance] presentAlert:alertController animated:YES];
}
@end
// MYAlertOperation will never complete.
// Because of an indirect dependency on operations being run on mainQueue.
// This example is an issue because mainQueue maxConcurrentOperationCount is 1.
// This example would not be an issue if maxConcurrentOperationCount was > 1.
[NSOperationQueue.mainQueue addOperation:[[MYAlertOperation alloc] init]];
更新3-示例2:
// VendorManager represents any class that you are not in direct control over.
@interface VendorManager : NSObject
@end
@implementation VendorManager
+ (void)doAnsyncVendorRoutine:(void (^)(void))completion {
// Need to do some expensive work, make sure we are off the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND 0), ^(void) {
// Some off main thread background work
sleep(10);
// We are done, go back to main thread
[NSOperationQueue.mainQueue addOperationWithBlock:completion];
});
}
@end
// MYAsyncBoilerPlateOperation represents all the boilerplate needed
// to implement a useful asnychronous NSOperation implementation.
@interface MYAlertOperation : MYAsyncBoilerPlateOperation
@end
@implementation MYAlertOperation
- (void)main {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:"Vendor"
message:"Should vendor do work?"
preferredStyle:UIAlertControllerStyleAlert];
__weak __typeof(self) weakSelf = self;
[alertController addAction:[UIAlertAction actionWithTitle:@"Yes"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[VendorManager doAnsyncVendorRoutine:^{
// implemented in MYAsyncBoilerPlateOperation
[weakSelf completeThisOperation];
}];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"No"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakSelf cancel];
}]];
[MYAlertManager sharedInstance] presentAlert:alertController animated:YES];
}
@end
// MYAlertOperation will never complete.
// Because of an indirect dependency on operations being run on mainQueue.
// This example is an issue because mainQueue maxConcurrentOperationCount is 1.
// This example would not be an issue if maxConcurrentOperationCount was > 1.
[NSOperationQueue.mainQueue addOperation:[[MYAlertOperation alloc] init]];
我没有展示MyAsyncBlockOperation的实现,但您可以使用
所以我想,为什么不让另一个NSOperationQueue呢?其中一个设置为前面提到的间接(仍遵循文档)。因此,我们可以拥有一个并发NSOperationQueue,合法地将目标对准串行主线程,并最终确保操作在主线程上运行
如果您需要澄清,请告诉我,以下是完整代码的示例:
NSOperationQueue *asyncSafeMainQueue = [[NSOperationQueue alloc] init];
asyncSafeMainQueue.qualityOfService = NSQualityOfServiceDefault; // not needed, just for clarity
dispatch_queue_t underlyingQueue = dispatch_queue_create("com.mydomain.main-thread", NULL);
dispatch_set_target_queue(underlyingQueue, dispatch_get_main_queue());
asyncSafeMainQueue.underlyingQueue = underlyingQueue;
现在。。。对于需要在主线程上运行的异步操作,有一个安全的操作队列,无需任何不必要的上下文切换。
它安全吗?我不明白为什么您认为
mainQueue
对于异步操作不安全。您给出的原因会使同步操作变得不安全(因为您可能会死锁)
无论如何,我认为尝试你建议的解决方法是个坏主意。苹果没有解释(在你链接的页面上)为什么不应该将underyingqueue
设置到主队列。我建议你谨慎行事,遵循禁令的精神而不是文字
更新
现在看看您更新的问题,以及示例代码,我看不到任何东西会阻塞主线程/队列,因此不存在死锁的可能性。mainQueue
的macConcurrentOperationCount
为1并不重要。在您的示例中,我没有看到任何需要创建单独的NSOperationQueue
或从中受益的内容
此外,如果underyingqueue
是一个串行队列(或在其目标链中的任何位置都有一个串行队列),那么将maxConcurrentOperationCount
设置为什么并不重要。操作仍将连续运行。你自己试试吧:
@implementation AppDelegate {
dispatch_queue_t concurrentQueue;
dispatch_queue_t serialQueue;
NSOperationQueue *operationQueue;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
concurrentQueue = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
serialQueue = dispatch_queue_create("q2", nil);
operationQueue = [[NSOperationQueue alloc] init];
// concurrent queue targeting serial queue
//dispatch_set_target_queue(concurrentQueue, serialQueue);
//operationQueue.underlyingQueue = concurrentQueue;
// serial queue targeting concurrent queue
dispatch_set_target_queue(serialQueue, concurrentQueue);
operationQueue.underlyingQueue = serialQueue;
operationQueue.maxConcurrentOperationCount = 100;
for (int i = 0; i < 100; ++i) {
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation %d starting", i);
sleep(3);
NSLog(@"operation %d ending", i);
}];
[operationQueue addOperation:operation];
}
}
@end
@实现AppDelegate{
调度队列\u t并发队列;
调度队列;
NSOperationQueue*操作队列;
}
-(无效)ApplicationIDFinishLaunching:(NSNotification*)通知{
concurrentQueue=调度队列创建(“q”,调度队列并发);
serialQueue=dispatch\u queue\u create(“q2”,无);
operationQueue=[[NSOperationQueue alloc]init];
//以串行队列为目标的并发队列
//调度\设置\目标\队列(concurrentQueue、serialQueue);
//operationQueue.underlyingQueue=concurrentQueue;
//以并发队列为目标的串行队列
调度\设置\目标\队列(串行队列、并发队列);
operationQueue.underlyingQueue=serialQueue;
operationQueue.maxConcurrentOperationCount=100;
对于(int i=0;i<100;++i){
NSOperation*操作=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@“操作%d正在启动”,i);
睡眠(3);
NSLog(@“操作%d结束”,i);
}];
[操作队列添加操作:操作];
}
}
@结束
Hmm。。如果使用setTarget
而不是指定的构造函数,则Swift-4会严重崩溃
如果使用Objective-C桥接,则可以执行以下操作:
@interface MakeQueue : NSObject
+ (NSOperationQueue *)makeQueue:(bool)useSerial;
@end
@implementation MakeQueue
+ (NSOperationQueue *)makeQueue:(bool)useSerial {
dispatch_queue_t serial = dispatch_queue_create("serial", nil);
dispatch_queue_t concurrent = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = useSerial ? serial : concurrent;
dispatch_set_target_queue(queue, dispatch_get_main_queue());
NSOperationQueue *opq = [[NSOperationQueue alloc] init];
opq.underlyingQueue = queue;
opq.maxConcurrentOperationCount = 8;
return opq;
}
@end
如果使用Swift,您有:
func makeQueue(_ useSerial: Bool) -> OperationQueue? {
let testCrash: Bool = false
var queue: DispatchQueue!
if testCrash {
let serial = DispatchQueue(label: "serial")
let concurrent = DispatchQueue(label: "concurrent", attributes: .concurrent)
queue = useSerial ? serial : concurrent
queue.setTarget(queue: DispatchQueue.main)
}
else {
let serial = DispatchQueue(label: "serial", qos: .default, attributes: .init(rawValue: 0), autoreleaseFrequency: .inherit, target: DispatchQueue.main)
let concurrent = DispatchQueue(label: "concurrent", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: DispatchQueue.main)
queue = useSerial ? serial : concurrent
}
let opq = OperationQueue()
opq.underlyingQueue = queue
opq.maxConcurrentOperationCount = 8;
return opq
}
现在我们测试它:
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Test Objective-C
let operationQueue = MakeQueue.makeQueue(false)!
operationQueue.addOperation {
self.download(index: 1, time: 3)
}
operationQueue.addOperation {
self.download(index: 2, time: 1)
}
operationQueue.addOperation {
self.download(index: 3, time: 2)
}
//Test Swift
let sOperationQueue = makeQueue(false)!
sOperationQueue.addOperation {
self.download(index: 1, time: 3)
}
sOperationQueue.addOperation {
self.download(index: 2, time: 1)
}
sOperationQueue.addOperation {
self.download(index: 3, time: 2)
}
}
func download(index : Int, time: Int){
sleep(UInt32(time))
print("Index: \(index)")
}
}
在任何情况下,maxConcurrentOperations
是什么似乎都无关紧要。。如果基础队列是串行的,则设置此值似乎没有任何作用。。但是,如果基础队列是并发的,则会限制一次可以运行的操作数
总之,一旦底层队列是MainQueue
或任何串行队列,所有操作都会被提交到它(串行)并被阻塞(它等待,因为它是串行队列)
如果我们已经在使用指定的队列,我不确定底层队列的意义是什么。。但在任何情况下,将其设置为main都会导致所有内容在主队列上连续运行,而与最大并发计数无关
这是我能找到的唯一用例。。并且您可以独立地恢复/挂起自定义队列上的任务,即使其基础队列是主队列或其他队列。挂起/恢复所有其他队列所针对的一个队列,将反过来挂起/恢复所有其他队列。“现在……有一个安全的操作队列,用于需要在主线程上运行的异步操作,并且没有任何不必要的上下文切换。”。。。我建议您描述在操作队列上运行的这些任务的关键方面。这感觉像是一个XY问题,您要求我们对您针对其他问题提出的解决方案发表意见。在不了解您在那里做什么的情况下,我们无法告诉您如何解决最初的挑战。任务是任何需要主线程的任务,也就是任何涉及UIApplication或其他UIKit对象的任务。或
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Test Objective-C
let operationQueue = MakeQueue.makeQueue(false)!
operationQueue.addOperation {
self.download(index: 1, time: 3)
}
operationQueue.addOperation {
self.download(index: 2, time: 1)
}
operationQueue.addOperation {
self.download(index: 3, time: 2)
}
//Test Swift
let sOperationQueue = makeQueue(false)!
sOperationQueue.addOperation {
self.download(index: 1, time: 3)
}
sOperationQueue.addOperation {
self.download(index: 2, time: 1)
}
sOperationQueue.addOperation {
self.download(index: 3, time: 2)
}
}
func download(index : Int, time: Int){
sleep(UInt32(time))
print("Index: \(index)")
}
}