首页 > 技术文章 > Thread 多线程

OrangesChen 2015-12-14 22:24 原文

Thread 多线程

1.多线程的概念

程序: 代码编译后, 形成执行文件

进程: 程序运行起来, 就是进程, 进程提供程序运行所需的端口等

线程: 分配资源

进程中至少要有一个线程(主线程), 如果主线程阻塞, 程序就会假死; 如果主线程停止运行, 程序将终止

2.没有使用多线程的应用, 有什么问题?

当处理复杂的数据操作时, 就会阻塞主线程

3.多线程的方式

a.NSObject

b.NSThread

c.NSOperationQueue

d.GCD

4.需不需要把所有的操作都放在子线程上?(主线程和子线程的区别)

a.能够调度资源大小不一样(主:10M, 子:5M)

b.子线程不能释放通过便利构造器创建的对象, 原因: 子线程没有自动释放池, autorelease的对象无法得到释放

c.刷新UI的操作必须在主线程中执行, 原因: 在子线程中刷新UI会失败

5.多线程的优缺点

优点: 可以为主线程分担操作, 避免出现主线程阻塞; 执行效率高

缺点: 资源消耗高; 资源抢夺; 需要管理

6.多个线程使用同一块资源时, 会出现资源抢夺, 解决方案: 当操作这块资源时, 加锁, 离开时, 解锁; 保证同一个时刻只能有一个线程操作资源

在story中添加button, 关联方法

    

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(rotation) userInfo:nil repeats:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (void)rotation {
    [UIView beginAnimations:@"旋转" context:nil];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    [UIView setAnimationDuration:0.15];
    self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, M_PI_4);
    [UIView commitAnimations];
}

- (IBAction)server:(id)sender {
    @autoreleasepool {
        for (NSInteger i = 1; i <= 1000; i++) {
            @autoreleasepool {
                NSString *string = [NSString stringWithFormat:@"接了%ld个客人", i];
                NSLog(@"%@", string);
            }
            //线程休眠
            [NSThread sleepForTimeInterval:1];
        }
        NSThread *currentThread = [NSThread currentThread];
        NSLog(@"%@", currentThread);
    }
  }

    NSObject中提供一些和线程相关的方法

    在主线程执行某些方法

    [self performSelectorOnMainThread: withObject: waitUntilDone:]

    在后台执行某些方法(在后台的子线程执行)

- (IBAction)serverWithNSObject:(id)sender {
//    在后台执行某些方法(在后台的子线程执行)
    [self performSelectorInBackground:@selector(server:) withObject:nil];
}

NSThread, 线程类, 继承于NSObject

- (IBAction)serverWithNSThread:(id)sender {
    //获取主线程
    NSThread *mainThread = [NSThread mainThread];
    NSLog(@"%@", mainThread);
    //线程所占的栈区大小, 4KB
    NSLog(@"%lu", mainThread.stackSize);
    //创建子线程
    NSThread *subThread = [[NSThread alloc] initWithTarget:self selector:@selector(server:) object:self];
    subThread.name = @"子线程";
//    [subThread start];
    NSLog(@"%@", subThread);
    NSLog(@"%lu", subThread.stackSize);
    //创建线程
    [NSThread detachNewThreadSelector:@selector(server:) toTarget:self withObject:nil];
    //线程休眠
//    [NSThread sleepForTimeInterval:<#(NSTimeInterval)#>]
    //获取当前线程
//    NSThread *currentThread = [NSThread currentThread];
//    NSLog(@"%@", currentThread);
}

    NSOperationQueue, 操作队列类, 继承于NSObject, 操作队列内存放各种操作, 这些操作最终在操作队列对应的线程上执行

    队列, 存放数据的方式: 先进先出(FIFO)

    NSOperation, 操作类, 可以存放在操作队列中, 抽象类, 操作只能执行一次, 它的子类有:

    NSInvocationOperation

    NSBlockOperation

- (void)eat {
    for (NSInteger i = 1; i < 100; i++) {
        NSLog(@"吃了%ld只龙虾", i);
    }
}

- (IBAction)serverWithNSOperationQueue:(id)sender {
    NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(eat) object:nil];
    //执行操作(在主线程中执行)
//    [invocation start];
    
    NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 1; i <= 100; i++) {
            NSLog(@"喝了%ld罐青岛啤酒", i);
        }
    }];
//    [block start];
    //操作建立依赖关系
    [invocation addDependency:block];
    //创建一个队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //最大并发数
    //并发数为1的队列, 叫串行队列
    //并发数大于1的队列, 叫并发队列
    queue.maxConcurrentOperationCount = 1;
    //向队列中添加操作(只有把操作添加到队列, 操作就会在队列对应的线程中执行)
//    [queue addOperation:invocation];
//    [queue addOperation:block];
    //同时添加多个操作
    [queue addOperations:@[invocation, block] waitUntilFinished:NO];
    //主队列, 绑定主线程
//    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
}

    GCD, Grand central Dispatch, 大中央调度, 多线程优化技术, 通过C语言实现, 执行效率高

- (IBAction)serverWithGCD:(id)sender {
    三种队列
   1.主调队列, 绑定主线程, 并发数为1
   dispatch_queue_t: 队列数据类型
   mianQueue: 变量名
   dispatch_get_main_queue: 调用函数
    dispatch_queue_t mianQueue = dispatch_get_main_queue();
    NSLog(@"%@", mianQueue);
    
    2.全局队列, 绑定子线程, 并发队列
    参数1: 优先级
    参数2: 预留参数, 为未来做准备, 一般写0
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"%@", globalQueue);
    
    3.自定义队列, 绑定子线程
    参数1: 队列的标识符, 通过调试工具可以查看
    参数2: 串行或并发
    dispatch_queue_t customQueu = dispatch_queue_create("com.huige", DISPATCH_QUEUE_SERIAL);
    NSLog(@"%@", customQueu);
    
    在队列中, 执行某些操作, 有两种方式:
    1.同步执行
    参数1: 操作执行队列
    参数2: block, 写你想要执行的操作
    dispatch_sync(globalQueue, ^{
        for (NSInteger i = 1; i <= 100; i++) {
            NSLog(@"%ld", i);
        }
    });
    
    2.异步执行
    dispatch_async(mianQueue, ^{
        for (NSInteger i = 1; i <= 100; i++) {
            NSLog(@"笑场%ld次", i);
        }
    });
    延迟多少秒执行某些操作
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"再见");
    });
    
//    dispatch_after(<#dispatch_time_t when#>, <#dispatch_queue_t queue#>, <#^(void)block#>)
    
}

创建单例

Singleton.h
#import <Foundation/Foundation.h>

@interface Singleton : NSObject

+ (Singleton *)sharedSingleton;

@end
Singleton.m
#import "Singleton.h"

@implementation Singleton
1.主线程创建
+ (Singleton *)sharedSingleton {
    static Singleton *singleton = nil;
    if (singleton == nil) {
        singleton = [[Singleton alloc] init];
    }
    return singleton;
}

2.互斥锁
+ (Singleton *)sharedSingleton { static Singleton *singleton = nil; //互斥锁 @synchronized(self) { if (singleton == nil) { singleton = [[Singleton alloc] init]; } } return singleton; }
3.dispatch_once 保证代码只执行一次
+ (Singleton *)sharedSingleton {
static Singleton *singleton = nil; 
//保证代码只执行一次, dispatch_once
static dispatch_once_t onceToken;
dispatch_once(
&onceToken, ^{ singleton = [[Singleton alloc] init];
});
return singleton;
}
@end

多线程购票

效果图:

 

具体代码: 在storyboard中添加一个label和开始售票的button, 关联方法和属性

#import "TicketViewController.h"

@interface TicketViewController ()
- (IBAction)start:(id)sender;
@property (strong, nonatomic) IBOutlet UILabel *label;

@property (nonatomic, assign) NSInteger totalCount;//总票数
@property (nonatomic, assign) NSInteger saleCount;//卖出的票数
@property (nonatomic, strong) NSLock *lock;//

@end

@implementation TicketViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.totalCount = 100;
    self.saleCount = 0;
    self.lock = [[NSLock alloc] init];
}

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

- (void)refreshUI {
    [UIView transitionWithView:self.label duration:0.2 options:UIViewAnimationOptionTransitionCurlUp animations:^{
        self.label.text = [NSString stringWithFormat:@"%ld", self.totalCount];
    } completion:^(BOOL finished) {
        [self.lock unlock];
    }];
   }

- (void)sale {
    while (YES) {
        [NSThread sleepForTimeInterval:0.5];
        [self.lock lock];
        if (self.totalCount > 0) {
            self.totalCount--;
            //在主线程中执行
            [self performSelectorOnMainThread:@selector(refreshUI) withObject:nil waitUntilDone:NO];
            self.saleCount++;
            NSLog(@"%@, 卖了%ld张票, 剩余%ld张票", [NSThread currentThread], self.saleCount, self.totalCount);
        } else {
            NSLog(@"票卖完了, 明年再回家吧!");
            return;
        }
    }
}


- (IBAction)start:(id)sender {
    //售票窗口(子线程)
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
    thread1.name = @"窗口1";
    [thread1 start];
    
    // 第二个售票窗口
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(sale) object:nil];
    thread2.name = @"窗口2";
    [thread2 start];
}
@end

 

推荐阅读