码迷,mamicode.com
首页 > 编程语言 > 详细

多线程 pthread + NSThread

时间:2015-12-22 22:40:26      阅读:289      评论:0      收藏:0      [点我收藏+]

标签:

 

多线程 pthread + NSThread

 
 


pthread (C语言)
· 规律: C语言中的数据类型一般都是以 _t或者Ref结尾 创建C语言的对象, 一般都用cretae#import <pthread/pthread.h>
- (IBAction)btnOnClick:(id)sender {
// 1.创建子线程
/*
第1个参数:线程代号 (现场对象)
第2个参数:线程的属性
第3个参数:子线程需要执行的操作(调用的方法)
void *(*)(void *)
返回值 (*指针名称)参数列表
void * 相当于OC中的 id
第4个参数:给第三个参数传递的值
*/
pthread_t threadId;
pthread_create(&threadId, NULL, demo, “L");
}

NSThread
· 一个 NSThread 对象就代表一个线程
· 创建线程的几种方式
? alloc / init
? 创建线程
NSThread *thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(demo:) object:@“L"];
// 设置线程名称
[thread setName:@"USER"];
//设置线程的优先级
//优先级仅仅说明被CPU调用的可能性大(运气不好,一次都没有)
[thread setThreadPriority:1.0]
// 2.启动线程
[thread start]
// 线程一启动,就会在线程thread中执行self的run方法

detach/performSelector
· 优点:简单快捷
· 缺点:无法对线程进行更详细的设置
// 1.创建线程
[NSThreaddetachNewThreadSelector:@selector(demo:) toTarget:self withObject:@“L"];

// 1.创建线程
// 注意: Swift中不能使用, 苹果认为这个方法不安全
[selfperformSelectorInBackground:@selector(demo:) withObject:@"L"];
主线程相关用法
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程

获得当前线程
NSThread *current = [NSThreadcurrentThread];
线程的名字
- (void)setName:(NSString *)name;
- (NSString *)name;
创建线程后自动启动线程
[NSThreaddetachNewThreadSelector:@selector(run) toTarget:selfwithObject:nil];
隐式创建并启动线程
[selfperformSelectorInBackground:@selector(run) withObject:nil];

上述2种创建线程方式的优缺点
优点:简单快捷
缺点:无法对线程进行更详细的设置


线程状态

// 启动线程
- (void)start;
// 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态

// 阻塞(暂停)线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
// 进入阻塞状态

// 强制停止线程
+ (void)exit;
// 进入死亡状态
相当于return 后面的代码一律不执行

// 注意:一旦线程停止(死亡)了,就不能再次开启任务

资源共享

· 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
· 比如多个线程访问同一个对象、同一个变量、同一个文件
· 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题



互斥锁
· 互斥锁使用格式
? @synchronized(锁对象) { // 需要锁定的代码 }注意:锁定1份代码只用1把锁,用多把锁是无效的
· 互斥锁的优缺点
? 优点:能有效防止因多线程抢夺资源造成的数据安全问题
? 缺点:需要消耗大量的CPU资源
· 互斥锁的使用前提:多条线程抢夺同一块资源
· 相关专业术语:线程同步
? 线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
? 互斥锁,就是使用了线程同步技术

原子和非原子属性

· OC在定义属性时有nonatomic和atomic两种选择
? atomic:原子属性,为setter方法加锁(默认就是atomic)线程安全,需要消耗大量的资源(多线程同时调用该属性时,会等正在执行的线程调用完,色图setter方法后再调用getter,保证了每次都只有一个线程在使用该属性)
? nonatomic:非原子属性,不会为setter方法加锁 非线程安全,适合内存小的移动设备


自旋锁 & 互斥锁共同点
· 共同点
? 都能够保证同一时间,只有一条线程执行锁定范围的代码
· 不同点
? 互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入"休眠"状态,等待其他线程执行完毕,打开锁之后,线程会被"唤醒"
? 自旋锁:如果发现有其他线程正在执行锁定的代码,线程会"一直等待"锁定代码执行完成!

自旋锁更适合执行非常短的代码!

iOS开发的建议
· 所有属性都声明为nonatomic
· 尽量避免多线程抢夺同一块资源
· 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力


线程间通信

· 什么叫做线程间通信
? 在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信

· 线程间通信的体现
? 1个线程传递数据给另1个线程
? 在1个线程中执行完特定任务后,转到另1个线程继续执行任务

· 线程间通信常用方法
? - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
? - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

PThread

void *demo(void *parm)
{
NSLog(@"%@", [NSThreadcurrentThread]);
// NSLog(@"%s", parm);

// 1.在主线程中执行耗时操作
for (int i = 0; i < 10000; i++) {
// 注意: NSLog是非常消耗性能的操作
NSLog(@"%i", i);
}
returnNULL;
}

- (IBAction)btnOnClick:(id)sender {

// 1.创建子线程
/*
第1个参数:线程代号 (现场对象)
第2个参数:线程的属性
第3个参数:子线程需要执行的操作(调用的方法)
void *(*)(void *)
返回值 (*指针名称)参数列表
void * 相当于OC中的 id
第4个参数:给第三个参数传递的值

规律:
C语言中的数据类型一般都是以 _t或者Ref结尾
创建C语言的对象, 一般都用cretae
*/
pthread_t threadId;
pthread_create(&threadId, NULL, demo, “L");
}

- (IBAction)btnOnClick:(id)sender {

NSLog(@"%s", __func__);
// 1.创建子线程
/*
Target: 子线程需要调用谁的方法
selector: 被子线程调用的方法
object: 调用方法时, 给方法传递的参数
*/
// 注意: 如果线程正在执行, 那么系统会自动强引用NSThread \
retained during the execution of the detached thread
// 当线程中的任务执行完毕, 系统会自动释放线程, 对线程进行一次release操作 \ hey are released when the thread finally exits.
NSThread *thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(demo:) object:@“L"];
thread.name = @"子线程1";
// 设置线程的优先级, 取值范围0.0~1.0, 1.0是最高, 默认就是0.5
// 注意点: 线程的优先级越高, 那么被CPU调度的可能性就越大
// 但是并不代表着, 优先级高的一定会先执行
thread.threadPriority = 0.0;
// 2.启动线程
// 注意点: 如果通过alloc/init创建NSThread, 那么需要手动启动线程
[thread start];

NSThread *thread2 = [[NSThreadalloc] initWithTarget:selfselector:@selector(demo:) object:@"lw"];
thread2.name = @"子线程2";
thread2.threadPriority = 1.0;
[thread2 start];

NSLog(@"主线程中当前的操作马上执行完毕了");
}

- (void)demo:(NSString *)str
{
//耗时操作: 1分钟
// NSLog(@"%@", [NSThread currentThread]);
// NSLog(@"%@", str);
// for (int i = 0; i < 90000; i++) {
// // 注意: NSLog是非常消耗性能的操作
// NSLog(@"%i", i);
// }

for (int i = 0; i < 100; i++) {
NSLog(@"%i - %@", i,[NSThreadcurrentThread]);
}
}


- (IBAction)btnOnClick:(id)sender {

NSLog(@"%s", __func__);

// 系统会自动在后台给我们开启一个子线程, 并且会自动启动该线程执行任务
[selfperformSelectorInBackground:@selector(demo:) withObject:@"oooo"];
}

- (void)test
{
/*
优点:
- 使用简便
- 如果通过detach方法创建线程, 不需要手动调用start启动线程 \
系统会自动启动
缺点:
- 不可以进行其它设置
- 通过detach方法创建子线程是没有返回值的
应用场景:
- 如果仅仅需要简单的开启一个子线程执行一些操作, 不需要对子线程进行其它设置, 那么推荐使用detach方法创建线程
*/
[NSThreaddetachNewThreadSelector:@selector(demo:) toTarget:selfwithObject:@"xxx"];
}

- (void)demo:(NSString *)str
{
//耗时操作: 1分钟
NSLog(@"%@ - %@", str,[NSThreadcurrentThread]);
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"%s", __func__);

// 1.创建线程
// 状态: 新建
NSThread *thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(demo) object:nil];

// 2.启动线程
// 状态: 就绪
[thread start];
}

- (void)demo
{
// 3.方法被调用了
// 状态: 运行
for (int i = 1; i <= 20; i++) {
NSLog(@"%i === %@", i,[NSThreadcurrentThread]);
if (i % 5 == 0 ) {
NSLog(@"睡一会");
// 告诉当前线程, 睡2秒
// 状态: 阻塞
// 注意: 在那个线程中调用sleepForTimeInterval, 那么哪个线程就会被睡
[NSThreadsleepForTimeInterval:2.0];
// 4001
// NSLog(@"%@", [NSDate distantFuture]);
// [NSThread sleepUntilDate:[NSDate distantFuture]];
}

if (i == 11) {
// 强制结束线程
// 状态: 消亡
// 注意:
// - 如果线程被强制退出了, 那么后面的代码就不会执行了
// - 在哪个线程中调用[NSThread exit], 那么强制结束的就是哪个线程
// 理解: [NSThread exit] == return
// [NSThread exit];
return; // 粗暴
}
}
NSLog(@"++++++++++");

} // 死亡/消亡


售票
- (void)viewDidLoad {
[superviewDidLoad];

// 0.初始化票数
self.totalCount = 100;
// 1.创建3个售票员
NSThread *thread1 = [[NSThreadalloc] initWithTarget:selfselector:@selector(saleTicket) object:nil];
thread1.name = @"售票员1";
self.thread1 = thread1;
NSThread *thread2 = [[NSThreadalloc] initWithTarget:selfselector:@selector(saleTicket) object:nil];
thread2.name = @"售票员2";
self.thread2 = thread2;
NSThread *thread3 = [[NSThreadalloc] initWithTarget:selfselector:@selector(saleTicket) object:nil];
thread3.name = @"售票员3";
self.thread3 = thread3;

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 2.让3个售票员同事售票
[self.thread1start];
[self.thread2start];
[self.thread3start];
}

// 售票方法
- (void)saleTicket
{
while (1) {

NSLog(@"欢迎光临");
// 只要被synchronized扩住的代码, 就是被锁住的代码 \
也就是说, 只要被synchronized{}扩住, 就能实现同一时刻, 只能有一个线程操作

/*
// 注意:
// 1. 如果多条线程访问同一个资源, 那么必须使用同一把锁才能锁住
// 2. 在开发中, 尽量不要加锁, 如果必须要加锁, 一定记住, 锁的范围不能太大, 哪里会有安全隐患就加在哪里
*/
// NSObject lockObj = [[NSObject alloc] init];
/*
技巧:
1.@synchronized单词的快速记忆方法 [NSUserDefaults standardUserDefaults] synchronize + d
2.开发中如果需要加锁, 一般都使用self
*/

// 线程2: 等待, 线程3: 等待
@synchronized(self){ // 锁住

// 1.查询剩余的票数
NSUInteger count = self.totalCount;
// 2.判断是否还有余票
if (count > 0) {
// 线程1 100
[NSThreadsleepForTimeInterval:0.1];
// 2.1卖票
self.totalCount = count - 1; // 99
NSLog(@"%@卖了一张票, 还剩%zd票", [NSThreadcurrentThread].name, self.totalCount);
}else
{
// 3.提示客户, 没有票了
NSLog(@"对不起, 没有票了");
break;
}
} // 解锁
}
}

- (NSObject *)lockObj
{
if (!_lockObj) {
_lockObj = [[NSObjectalloc] init];
}
return_lockObj;
}

线程间通信
@interfaceViewController ()

@property (weak, nonatomic) IBOutletUIImageView *imageView;
@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"%s", __func__);
/*
// 1.下载图片
// NSDate *begin = [NSDate date];
CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent();
NSURL *url = [NSURL URLWithString:@"http://pic.4j4j.cn/upload/pic/20130531/07ed5ea485.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
// NSDate *end = [NSDate date];
// NSLog(@"%f", [end timeIntervalSinceDate:begin]);
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(@"%f", end - begin);

// 2.将二进制转换为图片
UIImage *image = [UIImage imageWithData:data];

// 3.显示图片
self.imageView.image = image;
*/

// 开启一个子线程下载图片
[selfperformSelectorInBackground:@selector(downlod) withObject:nil];
}

- (void)downlod
{
NSLog(@"%@", [NSThreadcurrentThread]);
// 1.下载图片
NSURL *url = [NSURLURLWithString:@"http://pic.4j4j.cn/upload/pic/20130531/07ed5ea485.jpg"];
NSData *data = [NSDatadataWithContentsOfURL:url];
// 2.将二进制转换为图片
UIImage *image = [UIImageimageWithData:data];

// 3.跟新UI
#warning 注意: 千万不要在子线程中更新UI, 会出问题
// self.imageView.image = image;

/*
waitUntilDone:
YES: 如果传入YES, 那么会等待updateImage方法执行完毕, 才会继续执行后面的代码
NO: 如果传入NO, 那么不会等待updateImage方法执行完毕, 就可以继续之后后面的代码
*/
/*
[self performSelectorOnMainThread:@selector(updateImage:) withObject:image waitUntilDone:NO];

NSLog(@"------------");
*/

// 开发中常用
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];

// 可以在指定的线程中, 调用指定对象的指定方法
[selfperformSelector:@selector(updateImage:) onThread:[NSThreadmainThread] withObject:image waitUntilDone:YES];
}

- (void)updateImage:(UIImage *)image
{
NSLog(@"%@", [NSThreadcurrentThread]);
// 3.更新UI
self.imageView.image = image;

// for (int i = 0; i < 1000; i++) {
// NSLog(@"++++++++++++");
// }
}

@end




多线程 pthread + NSThread

标签:

原文地址:http://www.cnblogs.com/mshong1616/p/5068251.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!