【问题标题】:scheduleInRunLoop - threading network connectionsscheduleInRunLoop - 线程化网络连接
【发布时间】:2012-07-28 08:22:42
【问题描述】:

我没有找到任何合适的文档来解释 NSStream 的线程处理过程。具体来说,让我们使用 NSInputStream。 Objective-C 中的线程对我来说目前是一个谜,因为它看起来很简单。

我的问题主要是指这一行:

[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

您可以指定输入流将在其中运行的运行循环,我认为这很酷。问题是,如果我希望输入和输出流在它们自己的线程中运行,并且都在一个类中实例化,比如 Connection,那么如何让它们在它们自己的线程中运行?

我问的原因是因为代表。以前我们会使用[inputStream setDelegate:self],这意味着我们必须声明stream:handleEvent 来处理传入/传出数据。

所以最终我的问题是,如果你有一个设置输入和输出流的类,你如何同时处理每个流并将处理流事件的责任委托给当前类?

这里有一些代码可供选择:

[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];

我在想以下几点:

  • 您不能将责任委派给当前类中的两个线程,您必须委派给单独的对象。
  • 一个线程可以同时处理两个流? (我个人不这么认为,因为输入/输出会同时运行)
  • 我想错了,您可以创建一个单独的运行循环并针对某个单独的线程调用 scheduleRunLoop 吗?

有什么想法吗?

【问题讨论】:

  • 如果你创建了一个 NSThread,你也可以访问它拥有的 NSRunLoop。反过来也行不通,访问 NSRunLoop 不会为您创建 NSThread。如果您要求currentRunLoop,您将获得您所在线程的运行循环。

标签: objective-c multithreading cocoa nsrunloop nsstream


【解决方案1】:

注意heapstack之间的区别。

每个线程都有自己的堆栈,但所有线程都访问同一个堆。

输入流需要自己的线程,因为在读取流时没有到达任何 EOF,如果没有新字符出现,线程会阻塞并因此等待。所以为了防止你的应用阻塞输入流需要一个单独的线程。

继续,因为委托不能是静态方法,您必须复制或至少同步使用缓冲区才能返回结果。请记住,每个线程都有自己的堆栈,但都访问同一个堆。

NSStreamDelegate 是一个接口,允许您指定谁将管理来自流的事件。因此,允许您拆分编程以处理流和处理它们的事件。正如您可以将委托视为指向函数的指针,您必须确保它们在运行时存在,这就是为什么它们通常与协议一起使用和定义的原因。但是调用委托的方法并不意味着调用另一个线程的函数。您只需将参数/对象应用于将/必须在运行时存在的另一个对象/类的方法。

Apple 的NSThread 类和NSRunloop 类使它变得容易但令人困惑,因为运行循环与线程不同。 每个线程都可以有一个 runloop,它至少循环一次,并在无事可做时立即返回。使用[NSRunLoop currentRunLoop],您要求的是您所在线程的运行循环,而不是创建另一个线程。 因此,从同一个线程调用相同的 runloop 两次会导致在同一个线程中工作。这意味着如果一个部分正在阻塞,则您正在阻塞线程,因此同一线程中的其他部分也会等待。 (在最后一句中,你可以用 runloop 交换单词 thread 并且它仍然是一样的,它正在阻塞线程)

作为同时性问题,当输入和输出流应该在“同时”工作时,通常会涉及多个套接字端口。 NSInputStream 是只读的,NSOutputStream 是只写的。 为输入流提供自己的线程是明智的,因为远程发送者的性质给出的数据和时间可能会导致意外结果,而这是您无法控制的。您有责任定义运行循环(相应线程)在调用一次后是否应保持活动状态。

这样做你的输出流然后在一个/另一个线程中,你要求它的 current 运行循环。 您不能创建或管理运行循环,您只需请求它,因为每个线程都有一个,如果没有,则会为您创建一个。

在 iOS 上,您可以使用大量解决方案来实现您的个人设计模式,用于同时输入和输出流。 您可以使用NSThread 也可以使用-performSelectorInBackground:SEL withObject:(nullable Id),它实际上是NSThread.h 中定义的NSObject 的扩展。但是最后一个不允许您定义特殊的运行模式。

如果您不想根据需要创建服装 NSThread 子类,这里提到的一个简单解决方案也可能对您有用。 iOS how can i perform multiple NSInputStream 这里[NSRunLoop currentRunloop] 是被分离线程的runloop。也可以用块分离新线程

id<NSStreamDelegate> streamDelegate = //object that conforms to the protocol
[NSThread detachNewThreadWithBlock:^(void){
    NSInputStream *inputStream;
    [inputStream setDelegate:streamDelegate];
    // define your stream here
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                   forMode:NSRunLoopCommonModes];
    [inputStream open];
}];`

PS:@Ping 的示例是一个开关,用于在两个流的事件上进行交互,它不会同时进行流输入和输出。好吧,但是您可以在流及其事件上使用该示例,无论它们是否同时发生,典型的 NSStreamDelegate 东西。

【讨论】:

  • 不要忘记定义线程何时退出(通常在线程内)。
  • 这是一个很好的答案,有很多相关信息,但我以更好的方式解决了问题中的问题(“你如何让它们在自己的线程中运行?”)。
【解决方案2】:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventNone:
            break;
        case NSStreamEventOpenCompleted:
            break;
        case NSStreamEventHasBytesAvailable:
            [self _readData];
            break;
        case NSStreamEventHasSpaceAvailable:
            [self _writeData];
            break;
        case NSStreamEventErrorOccurred:
            break;
        case NSStreamEventEndEncountered:
            break;
        default:
            break;
    }
}

【讨论】:

  • -1 只是代码不是答案。你必须解释它。甚至只是对你为什么要做你正在做的事情的一点解释。
  • 这个答案不仅仅是粘贴代码;它可以在整个互联网上找到,但它没有回答问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-12
  • 1970-01-01
  • 2020-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多