zoukankan      html  css  js  c++  java
  • iOS 多线程学习笔记 —— NSThread

    本文复制、参考自文章:iOS多线程编程之NSThread的使用  ,主要为了加强个人对知识的理解和记忆,不做他用。原作者声明:

    著作权声明:本文由http://blog.csdn.net/totogo2010/原创,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

    这里对原作者的辛勤工作表示感谢!

    1. 简介

    1.1 iOS的多线程编程技术分类

    (1)NSThread 

      (2) Cocoa NSOperation

      (3) GCD (Grand Central Dispatch)

    这三种方式从上到下,抽象层次逐渐增高,使用也越来越简单。

    1.2 三种方式的优缺点

      优点   缺点
    NSThread 轻量 需要自己管理线程的生命周期,线程同步。线程同步加锁时,会有一定的系统开销。
    NSOperation   无需关心线程管理,数据同步,可以把精力放在自己需要的执行操作上  
    GCD iOS4.0后出现,以替代NSThread,NSOperation等技术的,很高效、强大  

    2. NSThread的使用

    2.1 创建方式

    (1) 实例方法创建

    - (id) initWithTarget:(id)target selector:(SEL)selector object:(id) argument
    示例: NSThread
    * myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil]; [myThread start];

      (2) NSThread 类方法创建

    + (void)detachNewThreadSelector:(SEL) aSelector toTarget:(id)aTarget withObject:(id)anArgument
    
    示例:
    [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];

    (3)NSObject 非显示方法创建

    + (void) performSelectorInBackground:(SEL)aSelector withObject:(id)anArgument
    示例
    [Obj performSelectorInBackground:@selector(doSomething:)withObject:nil];

    selector: 线程执行的方法,这个selector只能有一个参数,而且不能有返回值;

    target:    selector消息发送的对象

    argument:传输给selector的唯一参数,也可以是nil

    第一种方式会直接创建线程并且开始运行线程,第二种方法是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息。

     2.2 使用示例1: 异步加载图片

    (1)在viewDidLoad中创建子线程:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        NSThread* thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage:) object:kURL]; // 子线程执行方法 downloadImage
        [thread start]; // 启用子线程
    }

    (2)在线程执行方法 downloadImage 中执行加载数据操作,并使用performSelectorOnMainThread方法 通知主线程进行渲染操作。(线程调回主线程,并传递了数据)

    - (void)downloadImage:(NSString *) url
    {
        NSData* data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
        UIImage* image = [[UIImage alloc] initWithData:data];
        if(image == nil)
        {
            NSLog(@"image load failed...");
        }
        else
        {
            [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES]; // 与主线程通信,传递了image数据
        }
    }

      同样,还可以使用:  

    - (void)performSelector:(SEL)aSelector  onThread:(NSThread *)thread   withObject:(id)arg    waitUntilDone:(BOOL)wait    modes:(NSArray *)array

    方法来于其他线程通信。

    2.3 使用示例2: 多线程卖票

     (1) 定义public的数据结构,AppDelegate.h中

    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    {
        int tickets;
        int count;
        NSThread* ticketsThreadOne;
        NSThread* ticketsThreadTwo;
        NSCondition* ticketsCondition; 
        NSLock* theLock;
    }

    这里测试使用了NSCondition和NSLock两种加锁方法。

    (2)初始化, AppDelegate.m中

        tickets = 100;
        count = 0;
        theLock = [[NSLock alloc] init];
        // 锁对象
        ticketsCondition = [[NSCondition alloc] init];
        ticketsThreadOne = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
        [ticketsThreadOne setName:@"Thread-1"];
        [ticketsThreadOne start];
        
        ticketsThreadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
        [ticketsThreadTwo setName:@"Thread-2"];
        [ticketsThreadTwo start];

    (3)执行操作中,加锁,防止资源抢占

    - (void)run
    {
        while (TRUE)
        {
            // 上锁
    //        [theLock lock];
            [ticketsCondition lock];
            if (tickets >= 0)
            {
                [NSThread sleepForTimeInterval:0.09];
                count = 100 -tickets;
                NSLog(@"当前票数是:%d, 售出:%d, 线程名:%@", tickets, count, [[NSThread currentThread] name]);
                tickets--;
            }
            else
            {
                break;
            }
            [ticketsCondition unlock];
    //        [theLock unlock];
        }
    }

       NSCondition 可以使用 [ticketsCondition wait];等待,并由其他线程使用[ticketsCondition signal];唤起等待。

    2.4 使用@synchoronized

      我们还可以使用@synchoronized来简化NSLock的使用,这样就不必显示创建和调用NSLock对象,而自动创建了一个互斥锁(mutex lock),防止对资源的抢占。 如下

      

        obj1 = [[NSObject alloc]init]; // 两个线程,分别传递两个对象用于原操作标识
        obj2 = [[NSObject alloc]init];
        
        ticketsThreadOne = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:obj1]; 
        [ticketsThreadOne setName:@"Thread-1"];
        [ticketsThreadOne start];
        
        ticketsThreadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:obj2];
        [ticketsThreadTwo setName:@"Thread-2"];
        [ticketsThreadTwo start];
    - (void)run:(NSObject*) obj
    {
        @synchronized(obj)
        {
            while (TRUE)
            {
                // 上锁
        //        [theLock lock];
        //        [ticketsCondition lock];
                int currentTicketNum = [ticketCounter currentTicketNum];
                if ( currentTicketNum >= 0)
                {
                    [NSThread sleepForTimeInterval:0.09];
                    NSLog(@"当前票数是:%d, 售出:%d, 线程名:%@", currentTicketNum, ticketCounter.initTicketNum - currentTicketNum, [[NSThread currentThread] name]);
                    ticketCounter.currentTicketNum--;
                }
                else
                {
                    break;
                }
        //        [ticketsCondition unlock];
        //        [theLock unlock];
            }
        }
    }
  • 相关阅读:
    分布式01-Dubbo基础背景
    项目总结17-使用layui table分页表格
    项目总结16-创建验证码图片
    Springboot学习07-数据源Druid
    Springboot学习06-Spring AOP封装接口自定义校验
    Springboot学习05-自定义错误页面完整分析
    Springboot学习04-默认错误页面加载机制源码分析
    Springboot学习03-SpringMVC自动配置
    项目总结15:JavaScript模拟表单提交(实现window.location.href-POST提交数据效果)
    Springboot学习02-webjars和静态资源映射规则
  • 原文地址:https://www.cnblogs.com/wenshanzh/p/4039872.html
Copyright © 2011-2022 走看看