iOS 多线程 线程安全 线程间通信

一、什么是多线程

一个iOS程序就像一个圆,不断循环,直到将它切断。一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。iOS中的程序启动,创建好一个进程的同时,一个线程便开始运行,这个线程叫主线程。主线程在程序中的地位和其他线程不同,它是其他线程最终的父线程,且所有界面的显示操作即AppKit或UIKit的操作必须在主线程进行。 系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则共用进程的内存空间。每创建一个新的线程,都需要一些内存和消耗一定的CPU时间。另外当多个线程对同一个资源出现争夺的时候需要注意线程安全问题。

二、创建线程

1. 使用NSThread,创建一个NSThread的对象,调用其start方法。
// 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"http://b.png"];

//线程名字
thread.name = @"下载线程";

// 启动线程(调用self的download方法)
[thread start];
2. 使用+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument这个类方法创建一个线程并且会自动启动线程。
3. 使用NSObject 其实NSObject直接就加入了多线程的支持,允许对象的某个方法在后台运行。如:
//(调用self的download方法)
[self performSelectorInBackground:@selector(download:) withObject:@"http://c.gif"];

三、线程安全

多个线程可能会访问同一块资源很容易引发数据错乱和数据安全问题

解决方法:

互斥锁使用格式

@synchronized(锁对象) { // 需要锁定的代码 }

OC在定义属性时有nonatomic和atomic两种选择

atomic:原子属性,为setter方法加锁(默认就是atomic)

nonatomic:非原子属性,不会为setter方法加锁

atomic加锁原理

 @property (assign, atomic) int age;
 
 - (void)setAge:(int)age
 { 
 
     @synchronized(self) { 
        _age = age;
     }
 }

atomic:线程安全,需要消耗大量的资源

nonatomic:非线程安全,适合内存小的移动设备

iOS开发的建议:

1 .所有属性都声明为nonatomic

2 .尽量避免多线程抢夺同一块资源

3 .尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

四、线程间的通信

  • 线程间通信的体现
1 .一个线程传递数据给另一个线程

2 .在一个线程中执行完特定任务后,转到另一个线程继续执行任务
  • 线程间通信常用的方法
1. `NSThread`可以先将自己的当前线程对象注册到某个全局的对象中去,这样相互之间就可以获取对方的线程对象,然后就可以使用下面的方法进行线程间的通信了,由于主线程比较特殊,所以框架直接提供了在主线程执行的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

用法如下:

//点击屏幕开始执行下载方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self performSelectorInBackground:@selector(download) withObject:nil];
}

//下载图片
- (void)download
{    
    // 1.图片地址
    NSString *urlStr = @"http://d.jpg";
    
    NSURL *url = [NSURL URLWithString:urlStr];
    
    // 2.根据地址下载图片的二进制数据
   
    NSData *data = [NSData dataWithContentsOfURL:url];
    NSLog(@"---end");
    
    // 3.设置图片
    UIImage *image = [UIImage imageWithData:data];
    
    // 4.回到主线程,刷新UI界面(为了线程安全)
    [self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO];
    
   // [self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
    
    
}

- (void)downloadFinished:(UIImage *)image
{
    self.imageView.image = image;
    
    NSLog(@"downloadFinished---%@", [NSThread currentThread]);
}


2. `GCD`一个线程传递数据给另一个线程,如:
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    
{   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        NSLog(@"donwload---%@", [NSThread currentThread]);
        
        // 1.子线程下载图片
        NSURL *url = [NSURL URLWithString:@"http://d.jpg"];
        
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        UIImage *image = [UIImage imageWithData:data];
        
        // 2.回到主线程设置图片
        dispatch_async(dispatch_get_main_queue(), ^{
            
            NSLog(@"setting---%@ %@", [NSThread currentThread], image);
            
            [self.button setImage:image forState:UIControlStateNormal];
        });
    });
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么? 1...
    AlanGe阅读 1,805评论 0 17
  • 多线程 在iOS开发中为提高程序的运行效率会将比较耗时的操作放在子线程中执行,iOS系统进程默认启动一个主线程,用...
    郭豪豪阅读 2,618评论 0 4
  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,177评论 0 23
  • 原文地址 http://www.cnblogs.com/kenshincui/p/3983982.html 大家都...
    怎样m阅读 1,303评论 0 1
  • 亲爱的宝贝,妈妈爱你!我不要求你将来做成什么样的大事,我只希望你成为一个拥有美好品格的人!
    未完成的作品阅读 190评论 0 0