Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Cocoa touch 如何使用GCD处理NSStream运行循环调度带来的并发问题?_Cocoa Touch_Concurrency_Grand Central Dispatch_Nsstream_Nsrunloop - Fatal编程技术网

Cocoa touch 如何使用GCD处理NSStream运行循环调度带来的并发问题?

Cocoa touch 如何使用GCD处理NSStream运行循环调度带来的并发问题?,cocoa-touch,concurrency,grand-central-dispatch,nsstream,nsrunloop,Cocoa Touch,Concurrency,Grand Central Dispatch,Nsstream,Nsrunloop,我遇到了以下情况:我创建了一个GCD调度队列,并在其中调度一个NSStream到当前的nsrunlop,这是规范中要求它发出委托事件的,然后我使用[[nsrunlop currentlunloop run]运行该线程的运行循环 这会产生三种可能的情况: 创建一个串行队列,其中初始写入消息通过流发送,其他写入消息仅在有来自NSStream对象的委托回调时发送,因为尝试写入新消息时不考虑此模式(这是可取的)将失败,因为队列被运行循环锁定 创建一个并发队列,其中消息可以自由写入流,因为发送到队列的块将

我遇到了以下情况:我创建了一个GCD调度队列,并在其中调度一个
NSStream
到当前的
nsrunlop
,这是规范中要求它发出委托事件的,然后我使用
[[nsrunlop currentlunloop run]
运行该线程的运行循环

这会产生三种可能的情况:

  • 创建一个串行队列,其中初始写入消息通过流发送,其他写入消息仅在有来自
    NSStream
    对象的委托回调时发送,因为尝试写入新消息时不考虑此模式(这是可取的)将失败,因为队列被运行循环锁定

  • 创建一个并发队列,其中消息可以自由写入流,因为发送到队列的块将与运行运行循环的块同时执行。然而,虽然希望使写入消息和运行循环同时运行,但肯定不希望必须在队列中运行块ng正在同时尝试写入流

  • 创建两个队列——一个负责保持运行循环活动并从流回调接收读取,另一个负责向流发送异步写入消息。这似乎很理想,但是,
    NSStream
    文档明确指出,不应尝试读取/写入流将其计划在其中的线程反序列化


  • 考虑到这些场景,没有一个是理想的,如何解决这些问题?

    正如您在文档中所指出的,当您有一个基于运行循环的API,如
    NSStream
    ,一般的期望是,与该对象的所有交互都将发生在拥有运行循环的线程上。我不确定是否存在任何问题在使用
    NSStream
    时,最好混合使用这两种习惯用法(GCD和run循环)

    除了主队列之外,GCD没有线程关联的概念,因此除非调度
    NSStream
    的运行循环恰好是主线程运行循环,否则没有好的方法使用
    dispatch\u async
    来调度该线程上执行的块

    冒着陈述显而易见的风险,您可能应该只使用标准方法来调度其他线程上的方法。
    -performSelector:onThread:withObject:waituntldone:modes:
    是最明显的。如果您的困惑是想处理块,那么知道堆分配的块可以像Objecti一样处理会有帮助ve-C对象并实现
    -invoke
    选择器,就像
    NSInvocation
    s一样。与您的问题相关的一个简单示例可能如下所示:

    @interface AppDelegate ()
    {
        NSThread* bgthread;
    }
    @end
    
    @implementation AppDelegate
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    {
        // Basic loop to get the background thread to run until you call -cancel on it
        dispatch_block_t threadMain = [^{
            NSThread* thread = [NSThread currentThread];
            NSParameterAssert(![thread isMainThread]);
    
            NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
            NSPort* port = [NSPort port];
    
            // If we dont register a mach port with the run loop, it will just exit immediately
            [currentRunLoop addPort: port forMode: NSRunLoopCommonModes];
    
            // Loop until the thread is cancelled.
            while (!thread.cancelled)
            {
                [currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
            }
    
            [currentRunLoop removePort: port forMode: NSRunLoopCommonModes];
    
            [port invalidate];
            port = nil;
        } copy];
    
        // Start the thread
        bgthread = [[NSThread alloc] initWithTarget: threadMain selector: @selector(invoke) object: nil];
        [bgthread start];
    
        // Fetch the runloop, so you can schedule an NSStream on it...
        __block NSRunLoop* runloopForStream = nil;
        dispatch_block_t getrunloop = [^{
            runloopForStream = [NSRunLoop currentRunLoop];
        } copy];
    
        // Dispatch synchronously, so that runloopForStream is populated before we continue...
        [getrunloop performSelector: @selector(invoke) onThread: bgthread withObject: nil waitUntilDone: YES];
    
        // Schedule your stream, etc.
        NSOutputStream* mystream = ...; // Your code here...
        [mystream scheduleInRunLoop: runloopForStream forMode: NSDefaultRunLoopMode];
    
        // Then later, when you want to write some data...
        NSData* dataToWrite = [NSMutableData dataWithLength: 100];
        dispatch_block_t doWrite = [^{
            [mystream write: dataToWrite.bytes maxLength: dataToWrite.length];
        } copy];
    
        // Dispatch asynchronously to thread
        [doWrite performSelector: @selector(invoke) onThread: bgthread withObject: nil waitUntilDone: NO];
    }
    @end
    

    请注意,块的
    -copy
    是将它们复制到堆中所必需的,否则当声明方法超出范围时,它们将被解除分配。

    延迟到参与方,但您可以直接使用

    void CFReadStreamSetDispatchQueue(CFReadStreamRef stream, dispatch_queue_t q);
    void CFWriteStreamSetDispatchQueue(CFWriteStreamRef stream, dispatch_queue_t q);
    
    其中,CFReadStreamRef可以采用桥接NSInputStream,CFWriteStreamRef可以采用桥接NSOutputStream。这样,您根本不必计划或不计划运行循环,您的流将在后台运行

    来自以下内容的片段:

    在Swift中,您可以直接调用以下函数:

    CFReadStreamSetDispatchQueue(inputStream, streamQueue)
    CFWriteStreamSetDispatchQueue(outputStream, streamQueue)
    
    CFReadStreamSetDispatchQueue(inputStream, streamQueue)
    CFWriteStreamSetDispatchQueue(outputStream, streamQueue)