Ios 以下是dispatch_set_target_queue()的安全用法吗?

Ios 以下是dispatch_set_target_queue()的安全用法吗?,ios,nsoperationqueue,foundation,libdispatch,dispatch-queue,Ios,Nsoperationqueue,Foundation,Libdispatch,Dispatch Queue,我想做的是创建一个间接的 我的最终目标是将队列用作的属性。虽然使用了间接队列,但它在技术上遵循文档 之所以这样做,是因为对于异步操作来说并不安全,因为它是全局可访问的,并且设置为1。因此,你可以很容易地用这个操作队列击中自己的脚 更新1 关于这个问题假设的“异步NSO操作”的基础,似乎有很多混淆。需要明确的是,这是基于本文中的概念,特定的概念是使用“操作准备就绪”和依赖关系管理来管理应用程序中的任务,这意味着将异步NSOperations添加到NSOperationQueues以利用这一点。如果

我想做的是创建一个间接的

我的最终目标是将队列用作的属性。虽然使用了间接队列,但它在技术上遵循文档

之所以这样做,是因为对于异步操作来说并不安全,因为它是全局可访问的,并且设置为1。因此,你可以很容易地用这个操作队列击中自己的脚

更新1

关于这个问题假设的“异步NSO操作”的基础,似乎有很多混淆。需要明确的是,这是基于本文中的概念,特定的概念是使用“操作准备就绪”和依赖关系管理来管理应用程序中的任务,这意味着将异步NSOperations添加到NSOperationQueues以利用这一点。如果你把这些概念带到这个问题的精神上来,希望推理会更有意义,你可以专注于将解决方案与其他解决方案进行比较和对比

更新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]];
更新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)")
    }
}