iOS两个线程间嵌套发送同步消息

 先上代码,主要逻辑可看注释。最好是直接下载demo再往下看了。demo下载地址:http://download.csdn.net/detail/hursing/5159144

@implementation ViewController 

#define kLevelsOfNesting 5
NSString *const kParameter = @"Parameter";
NSString *const kRunLoop = @"RunLoop"; 

- (void)viewDidLoad
{
    [super viewDidLoad];
    // 在界面上增加一个button,点击后才触发发送消息的流程
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setTitle:@"click me" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    button.frame = CGRectMake(50, 50, 100, 30);
    [self.view addSubview:button];
    [NSThread mainThread].name = @"Main Thread";
    // 创建一个worker线程
    m_thread = [[NSThread alloc] initWithTarget:self selector:@selector(secondaryThreadMain:) object:nil];
    [m_thread start];
} 

- (void)sendSynchronousMessageToTheOtherThread:(NSMutableDictionary*)info
{
    // 不能存NSRunLoop进去,它非线程安全
    NSValue *runLoop = [NSValue valueWithPointer:CFRunLoopGetCurrent()];
    [info setObject:runLoop forKey:kRunLoop];
    NSThread *targetThread = [NSThread isMainThread] ? m_thread : [NSThread mainThread];
    [self performSelector:@selector(executeOperation:) onThread:targetThread withObject:info waitUntilDone:NO];
    CFRunLoopRun();  // 开始一个嵌套的RunLoop
} 

- (void)executeOperation:(NSMutableDictionary*)info
{
    NSNumber* number = [info objectForKey:kParameter];
    CFRunLoopRef runLoop = [(NSValue*)[info objectForKey:kRunLoop] pointerValue];
    int count = [number intValue];
    NSLog(@"count is reduced to %d on %@", --count, [NSThread currentThread].name);
    if (count)
    {
        number = [NSNumber numberWithInt:count];
        [info setObject:number forKey:kParameter];
        [self sendSynchronousMessageToTheOtherThread:info];
    }
    CFRunLoopStop(runLoop);  // 停止掉RunLoop栈栈顶的RunLoop
} 

- (void)buttonClicked:(UIButton*)button
{
    static bool mainThreadFirst = false;
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kLevelsOfNesting], kParameter, nil];
    mainThreadFirst = !mainThreadFirst;
    if (mainThreadFirst)
    {
        [self sendSynchronousMessageToTheOtherThread:dictionary];
        NSLog(@"-------Because of nesting synchonization, this log is printed at the end.-------");
    }
    else
        [self performSelector:@selector(sendSynchronousMessageToTheOtherThread:) onThread:m_thread withObject:dictionary waitUntilDone:NO]; 

} 

- (void)secondaryThreadMain:(id)para
{
    @autoreleasepool {
        [NSThread currentThread].name = @"Secondary Thread";
        NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
        NSThread *thread = [NSThread currentThread];
        NSPort *port = [NSMachPort port];       // 阻塞RunLoop
        [currentRunLoop addPort:port forMode:NSDefaultRunLoopMode];
        NSDate *distantFuture = [NSDate distantFuture]; 

        while (!thread.isCancelled)
        {
            NSAutoreleasePool *pool = [NSAutoreleasePool new];
            NSLog(@"RunLoop runs once");
            [currentRunLoop runUntilDate:distantFuture]; // 这里会阻塞,直到有事件触发
            [pool drain];
        } 

        [currentRunLoop removePort:port forMode:NSDefaultRunLoopMode];
    }
} 

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
} 

@end 

运行demo工程,点击一下button,看看log输出就懂有什么用了。或者在第61行

CFRunLoopStop(runLoop);  // 停止掉RunLoop栈栈顶的RunLoop 

加个断点,看看堆栈,会在两个线程都看到嵌套的RunLoop.堆栈截图如下:

实用例子

子线程想创建一个UIView,发同步消息到主线程创建(UIKit对象都得主线程操作),主线程创建过程中又需要去子线程执行一段代码做些判断,这又需要主线程发同步消息回子线程;如果使用锁技术,这就是死锁。

RunLoop的东西很复杂,认真看文档最实际了。也可以重点看CFRunLoopRun和CFRunLoopStop两个函数。

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1

附:

关于NSRunLoop和CFRunLoop需要注意的地方:

Warning:  The NSRunLoop class is generally not considered to be thread-safe and its methods should only be called within the context of the current thread. You should never try to call the methods of an NSRunLoop object running in a different thread, as doing so might cause unexpected results.
Although they are not toll-free bridged types, you can get a CFRunLoopRef opaque type from an NSRunLoop object when needed. The NSRunLoop class defines a getCFRunLoop method that returns a CFRunLoopRef type that you can pass to Core Foundation routines. Because both objects refer to the same run loop, you can intermix calls to the NSRunLoop object and CFRunLoopRef opaque type as needed.
Thread safety varies depending on which API you are using to manipulate your run loop. The functions in Core Foundation are generally thread-safe and can be called from any thread. If you are performing operations that alter the configuration of the run loop, however, it is still good practice to do so from the thread that owns the run loop whenever possible.

The Cocoa NSRunLoop class is not as inherently thread safe as its Core Foundation counterpart. If you are using the NSRunLoop class to modify your run loop, you should do so only from the same thread that owns that run loop. Adding an input source or timer to a run loop belonging to a different thread could cause your code to crash or behave in an unexpected way.
时间: 2024-09-16 23:33:53

iOS两个线程间嵌套发送同步消息的相关文章

java 两台服器间的缓存同步

问题描述 系统(java开发的系统)运行环境:websphere服务器,两台AIX操作系统作负载均衡现在项目中有这么一个需求:用户进入我们的系统后,更改了数据,而这些更改的数据我们系统会自动加到缓存(这里的缓存其实就是一个静态变量)中去,现在有什么办法能让它在一台服务器上更改缓存数据后自动同步到另一台服务器上,我在网上搜索了一下,貌似有个JMS可以,不过看上去有点复杂,高手些有什么好的建议么,非常感谢!! 解决方案 解决方案二:该回复于2011-10-27 16:52:35被版主删除

如何用管道实现线程间多次通信,不是一次

问题描述 最近看到管道这里,自己写了个用管道来实现两个线程通信的程序,但是不知道为什么第一次通信可以成功,写线程可以写入,读线程可以读出,第二次就出现异常,异常信息是Readenddead.看网上很多人讲是因为第二次通信时读线程已经死亡.请问如何能够用管道实现多次通信? 解决方案 解决方案二:while能解决吗不过我发现用管道流在两个线程间来回传递数据,效率非常低,不知道的还以为加了延迟解决方案三:我就是想知道你是怎么实现的..代码...就像我说我的代码总是报异常空指针一样,你知道大体上为什么会

浅析iOS应用开发中线程间的通信与线程安全问题_IOS

线程间的通信  简单说明 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信   线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任务后,转到另1个线程继续执行任务   线程间通信常用方法 复制代码 代码如下: - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; - (void)performSelector

android-Android两个子线程同步问题

问题描述 Android两个子线程同步问题 现在Android端有这么一个需求,将Android的屏幕不停地截图然后通过Socket发送,现在希望能将截图和发送分为两个线程,有没有哪位能教教我啊,因为刚做Android没有多久,最好能有一个小Demo讲解一下,例如一边截图,一边保存的Demo,非常感谢; 解决方案 建立一个的消息队列做图片顺序控制和数据缓冲,截图线程截图后把截图(对象或者图片路径)从队尾插入,发送线程从队头取图片,队列满截图线程等待,队列空发送线程等待,设计的时候,应该根据设备的

线程间协作的两种方式:wait、notify、notifyAll和Condition

在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态.然后等待消费者消费了商品,然后消费者通知生产者队列有空间了.同样地,当

深入解析Java的线程同步以及线程间通信_java

Java线程同步 当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用.达到此目的的过程叫做同步(synchronization).像你所看到的,Java为此提供了独特的,语言水平上的支持. 同步的关键是管程(也叫信号量semaphore)的概念.管程是一个互斥独占锁定的对象,或称互斥体(mutex).在给定的时间,仅有一个线程可以获得管程.当一个线程需要锁定,它必须进入管程.所有其他的试图进入已经锁定的管程的线程必须挂起直到第一个线程退出管程.这些其他的线程被

iOS提问:两个应用间跳转,怎么写跳回之前的应用的方法

问题描述 iOS提问:两个应用间跳转,怎么写跳回之前的应用的方法 各路大神,请教下,当购物APP调用支付宝或微信支付时,不管支付成功与失败,都可以回到购物APP跳转之前的页面,微信和支付宝是怎么处理回到购物APP的,这个跳回购物APP时,购物APP也没有出现启动页,感觉是直接切换的应用,我自己用OpenURL练习时,应用A跳应用B,(不管B启动没启动B都会出现启动页,为什么B在后台的时候还是会出现启动页呢),我要在应用B上加一个button来返回应用A,用的方法还是OpenURL,(这样跳回应用

android-Android线程间同步问题,实现主线程死等新线程处理结束

问题描述 Android线程间同步问题,实现主线程死等新线程处理结束 我的主线程是activity,在主线程里开启了线程R1,希望R1的任务处理完成后通知主线程继续,否则主线程一直等待,怎么实现? 解决方案 首先你这样的需求是违背android官方设计的,你应该考虑一下要实现你的功能,换一种需求(实现方式)是否可以. 主线程负责呈现画面增强交互,不应该阻塞,非要实现这样的功能, 可以在主线程是activity开启线程R1,然后什么事儿也不做,当R1的任务处理完成后通知主线程继续后再做事儿(绘制画

android-Android,在两个线程里发送notification,第二个notification弹出两次

问题描述 Android,在两个线程里发送notification,第二个notification弹出两次 下面是我写的demo,可以完全显示问题. 我的app是发送一个地址到PC,先通知"正在发送..",在另一个线程中执行发送,完成后先cancel掉之前的"正在发送",再notify一个"发送成功"通知.可结果,"发送成功"通知在状态栏弹出了两次.这个问题想了3天,求大神帮忙. package com.teana.teana