iOS 多线程学习笔记 —— NSThread

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

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

这里对原做者的辛勤工做表示感谢!编程

1. 简介

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

(1)NSThread 数据结构

  (2) Cocoa NSOperation多线程

  (3) GCD (Grand Central Dispatch)app

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

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 类方法建立url

+ (void)detachNewThreadSelector:(SEL) aSelector toTarget:(id)aTarget withObject:(id)anArgument

示例:
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];

(3)NSObject 非显示方法建立spa

+ (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];
        }
    }
}