多线程工具之NSThread

一个NSThread对象就是一个线程

1.创建线程

 

类存储在堆内存中,对象存储在栈内存中


/ / 是否是多线程

[NSThread isMultiThreaded]


//是否是主线程

[NSThread isMainThread]


//是否是当前线程

[NSThread currentThread]

开启新的线程的四种方法


//1.使用NSThread类方法?detach方法,隐式创建  addAction12就是该线程的入口方法

//注意: 每个线程都有main函数,这里相当于在main函数中self调用了addAction12方法

//这种方式的缺点是非常的不灵活,没办法操作thread,开启时间点也没办法控制

[NSThread detachNewThreadSelector:@selector(addAction12) toTarget:self withObject:nil];


//2.使用NSObject的线程扩展perform方法
      //隐式方法 与上一个方法使用类似

//    [self performSelectorInBackground:@selector(addAction12) withObject:nil];


//3.创建一个NSThread对象,然后调用start方法执行:
    //显式创建
    NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(addAction3) object:nil];
    thread.name = @"thread_name";

//配置线程栈空间

//在线程开始之前 设置栈空间才有效,不能使用创建线程的第一和第二种方法
    thread.stackSize = 100;

//配置线程的本地存储

//线程的全局数据 是readOnly
    [thread.threadDictionary setObject:@"value1" forKey:@"key1"];
   
    //错误写法,这是set方法,readOnly不能用
//    thread.threadDictionary = [[NSMutableDictionary alloc]init];

//线程的优先级

//范围是0-1,最高是1,iOS8中更新时被qualityOfService代替

/ /根据线程的优先级来决定线程的执行顺序。

thread.threadPriority = 1.0;
    thread.qualityOfService = NSQualityOfServiceUserInteractive;//不能再线程start后修改

//开启线程(现在才真正的创建出一个新的线程)

[thread start];


//4.创建一个NSThread子类,然后实例化调用start方法

//子类NSThread,重写main方法,,率先进入CNThread的main方法

/ /显示创建

CNThread* cnThread = [[CNThread alloc]init];

[cnThread start];


/ /在当前线程中调用

/ /就是viewDidLoad在哪个线程中,直接调用的方法就在哪个线程中

[self addAction12];

[self performSelect…..];

2.设置线程的Detached、Joinable状态

脱离线程(Detach Thread)---线程完成后,系统自动释放它所占用的内存空间

可连接线程(Joinable Thread)---一线程完成后,不回收可连接线程的资源

通过NSThread创建的线程都是Detached的。如果你想要创建可连接线程,唯一的办法 是使用 POSIX 线程。POSIX 默认创建的 线程是可连接的。通过 pthread_attr_setdetachstate函数设置是否脱离属性

3.完善线程的入口

 

1.autorelase

2.runloop

runloop可以长期循环运行,但我们已可以自己定义一个循环,让它保持长期运行,但是如果不用runloop,中间线程不会休息cpu损耗过大,要想让程序每隔3秒执行一次,就需要添加一个nstimer,但是如果没有循环timer一运行就会被杀死,所以必须有timer也必须有循环,但是循环进去后,又会一直卡死在循环里出不去,


- (void)viewDidLoad {
    [super viewDidLoad];
    NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadAction) object:nil];
    [thread start];
    //如果直接在当前线程中调用循环,就会卡死在这,不会进入程序
//    while (true) {
//          [self action];
//    }
 
}
- (void)threadAction{
    @autoreleasepool {
        //新线程不会干扰到主线程
        [[NSThread currentThread].threadDictionary setObject:@(false) forKey:@"isEixt"];
        while (true) {
            if ([[[NSThread currentThread].threadDictionary valueForKey:@"isEixt"]boolValue]) {
                return;
            }
            //在新线程中调用
            [self action];
        }
    }
}
- (void)action{
    count++;
    if (count == 10000) {
        [[NSThread currentThread].threadDictionary setObject:@(true) forKey:@"isEixt"];
    }
    NSLog(@"-----,%d",count);

}

 

因上总结我们就得用到runloop了

主线程有一个runloop默认是开启的,但其他线程也有自己的一个runloop默认是关闭的,所以需要我们手动开启


 - (void)threadAction{
    @autoreleasepool {
        //新线程不会干扰到主线程

[[NSThread currentThread].threadDictionary setObject:@(false) forKey:@"isEixt"];

NSTimer* timer =  [NSTimer timerWithTimeInterval:3 target:self selector:@selector(action) userInfo:nil repeats:YES];

//添加时间源
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        //开启loop方法

[[NSRunLoop currentRunLoop]run];

}

//timer是run起来了,但是怎么终止呢,下面来讲解一下

4.终止线程

终止线程不要用POSIX直接杀死线程,会照成内存泄漏

最好的方式:让线程接收取消和退出消息

 


- (void)threadRoutine{

@autoreleasepool {

//每一次的 NSRunloop循环都检查退出条件是否为YES,如果为YES退出循环回收资源,如果为NO,则 进入下一次NSRunloop循环。

BOOL exitNow = NO;

//是否有更多的任务

BOOL moreWorkToDo = YES;

NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
        NSMutableDictionary* threadDict = [[NSThread currentThread]
                                           threadDictionary];
        [threadDict setValue:[NSNumber numberWithBool:exitNow]
                      forKey:@"ThreadShouldExitNow"];

NSTimer* timer =  [NSTimer timerWithTimeInterval:3 target:self selector:@selector(action) userInfo:nil repeats:YES];

//添加时间源

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

//终止线程

while (moreWorkToDo && !exitNow)

{

//执?行线程真正的工作方法,如果完成了可以设置moreWorkToDo为False,就是不执行更多的任务了

//打开了runloop,runloop又监听timer,当timer的action执行完以后,该语句才会执行完

[runLoop runUntilDate:[NSDate date]];

exitNow = [[threadDict valueForKey:@"ThreadShouldExitNow"]
                       boolValue];
        }
    }

}


- (void)action{

count++;

if (count == 3) {
        [[NSThread currentThread].threadDictionary setObject:@(true) forKey:@"ThreadShouldExitNow"];
    }
    NSLog(@"-----,%d",count);

}

//执行结果每隔3秒运行一次,执行三次后退出循环

runUntilDate:方法换为runMode:beforeDate:方法

//如果使用runUntilDate:,代码会一直进入循环里边出不来,一直调用action方法,无法进入runloop里面,要想进入runloop里面(循环不一直在循环里边),我们把方法改为了runMode:beforeDate:设置一个超时时间,使得代码不会马上就跑完,一直在跑,跑完之后就等待

//此时又出现了新的问题,进入了runloop后又出不来了,所以我们又加了CFRunLoopStop(CFRunLoopGetCurrent());使得runloop停止,停止后,就可以返回到runMode:beforeDate:,while循环就可以继续工作了


  //一次Timer事件触发处理后, 这个RunLoop要想有返回值就要用runmode方法

//如果在action中,没有停止RunLoop的操作的话,RunLoop 一直在运行,下面这行代码 永远不会有返回值

BOOL res = [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

NSLog(@"%d",res);


//    核心代码

//这行代码写在timer的触发事件中

/ /runloop停止

CFRunLoopStop(CFRunLoopGetCurrent());

 

这时候再在while循环中加入[self doOtherAction];此时就能实现交替循环了


[self doOtherAction];

- (void)doOtherAction{

NSLog(@"++++++");

}

//此循环实现了当主线程处于空闲时间时,可以执行其他线程

 

5.取消线程

子类化一个NSThread,在子类中重写main方法,使得线程率先进入main


-(void)main
{
    @autoreleasepool {
        NSLog(@"starting thread.......");
        NSTimer *timer = [NSTimer timerWithTimeInterval:1
                                                 target:self selector:@selector(doTimerTask) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer
                                    forMode:NSDefaultRunLoopMode];
        //如果当前线程不被取消,则进入循环
        while (!self.isCancelled) {
            [self doOtherTask];
           
//           如果在doTimerTask中,没有停止RunLoop的操作的话,RunLoop 一直在运行,下面这行代码 永远不会有返回值
            //runloop开始执行
            BOOL ret = [[NSRunLoop currentRunLoop]
                        runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

NSLog(@"after runloop counting.........: %d", ret);

}

NSLog(@"finishing thread.........");
    }

}


- (void)doTimerTask
{
    NSLog(@"do timer task");

//    添加RunLoop停止代码,使NSRunLoop runMode:(NSString *)mode beforeDate:(NSDate *)limitDate方法返回

 / /停止代码必须在runloop运行接口为runMode:下才能用

//    这行代码写在 timer的触发事件中
    CFRunLoopStop(CFRunLoopGetCurrent());

}
- (void)doOtherTask
{
    NSLog(@"do other task");

}


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //4秒过后取消线程
        [thread cancel];

});

效果图对比如下:

图1:当你不添加runloop停止代码时,进入runloop的代码出不来,会一直执行timer的doTimerAction方法。。。

图2:添加runloop停止代码后,停止后,就可以返回到runMode:beforeDate:,while循环就可以继续工作了

图3:加入线程取消代码,该线程就会延迟4秒后取消

 

6.runloop的运行接口


//运? NSRunLoop,运?行模式为默认的NSDefaultRunLoopMode模式,运行起来就永远不会停止

- (void)run;


//运?NSRunLoop: 参数为运行时间期限,运?行模式为默认的NSDefaultRunLoopMode模式

-(void)runUntilDate:(NSDate *)limitDate;


//运?NSRunLoop: 参数为运?行模式时间期限,返回值为YES(1)表?示是处理事件后返回的,NO(0)表?示是超时或者停?止运?行导致返回的

- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

7.线程同步

在多个线程访问相同的数据时,有可能会造成数据的冲突。比如常见的售票问题。

/ /首先创建3个线程,每个线程都调用sellticket方法,初始值ticket为100;


//卖票

- (void)sellTicket{

//获取当前的票数

int current = _ticket;

//若当前票数为0,说明票已经卖完,跳出循环

if (current == 0) {
        NSLog(@"ticket:%d,sold:%d",_ticket,_sold);
        return;
    }
    //买票延时

usleep(10000);

//每循环一次,票数久等于当前票数减一

_ticket = current- 1;

NSLog(@"---%@--,%d",[[NSThread currentThread]name],_ticket);

//每循环一次,卖出的票数就加一

_sold++;

//递归调用

[self sellTicket];

}

效果如图

显而易见这个程序是有问题的,每张票在三个窗口都会卖一次,最后卖的票数会多出总票数,要想解决该问题,就要用到数据同步锁

8.数据同步锁(NSLock)

加锁前需要先初始化


//加锁

[_lock lock];

//解锁

[_lock unlock];

9.数据等待(NSCondition)

A线程需要等待B线程执行后的某个结果继续执行,也就是同步 问题,这时就会需要A等待B,解决方式如下:


- (void)viewDidLoad {
    [super viewDidLoad];

_lock = [[NSCondition alloc]init];

[self performSelectorInBackground:@selector(cook) withObject:nil];

[self performSelector:@selector(buyStuff) withObject:nil afterDelay:4];

}


- (void)cook{

[[NSThread currentThread]setName:@"cook_thread"];

NSLog(@"开始做饭");

NSLog(@"发现没菜,需要去买菜");

[_lock lock];
    [_lock wait];
    usleep(1000000);
    NSLog(@"买菜回来,开始做饭");

}


- (void)buyStuff{
    NSLog(@"买菜进行中。。。。");
    //发送信号

[_lock signal];

}

10.nonatomic和atomic

在@property中最直观的用法就是生成set和get方法

readOnly 只有get方法

atomic:默认是有该属性的,这个属性是为了保证程序在多线程情况下,编译器会自动生成一 些互斥加锁代码,避免该变量的读写不同步问题。

nonatomic:如果该对象无需考虑多线程的 情况,请加入这个属性,这样会让编译器少生成一些互斥加锁代码,可以提高效率。

时间: 2024-10-12 10:50:31

多线程工具之NSThread的相关文章

多线程编程 (1) -NSThread

多线程编程 (1) -NSThread 每个iOS应用程序都有个专门用来更新显示UI界面.处理用户触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验.一般的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程编程是防止主线程堵塞,增加运行效率的最佳方法. iOS中有3种常见的多线程编程方法: 1.NSThread 这种方法需要管理线程的生命周期.同步.加锁问题,会导致一定的性能开销 2.NSOperation和NSOpe

IOS 多线程编程之 NSThread 的使用

1.简介: IOS 多线程编程之 NSThread 的使用 1.1 IOS 有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation (IOS 多线程编程之 NSOperation 和 NSOperationQueue 的使用) 3.GCD 全称:Grand Central Dispatch( IOS 多线程编程之 Grand Central Dispatch(GCD)介绍和使用) 这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也

IOS 多线程02-pthread 、 NSThread 、GCD 、NSOperationQueue、NSRunLoop

注:本人是翻译过来,并且加上本人的一点见解. 要点: 1.前言 2.pthread 3.NSThread 4.Grand Central Dispatch(GCD) 5.Opearation Queues 6. Run Loops 7. 多线程编程中面临的挑战 8. 资源共享 9. 互斥锁 10. 死锁 11. 资源饥饿(Starvation) 12. 优先级反转 1. 前言 其实把RunLoop叫做多线程不正确,因为它不能真正的并行,不过因为它与并发编程有莫大关系,所以值得我们深入了解. 2.

网络多线程(pthread , NSThread,GCD ,NSOperation)

在这里给大家介绍一些多线程的知识,以及应用,希望能给一些需要的朋友学习学习,如果有错误的地方,请帮忙指出,非常感谢. 那么先介绍多线程前,先说一下什么是线程,什么是进程? 进程:{ 1.正在运行的一个应用程序就叫进程. 2.每个进程之间都是相互独立的,每个进程都运行在自己独立的专用的且受保护的内存空间内. } 线程:{  1.线程是进程的基本执行单元. 2.每一个进程都默认开启一条线程,我们称之为主线程.(一个进程至少有一条线程) } 多线程:多线程就是一个进程可以开辟多线线程(子线程),同时执

iOS开发之多线程技术(NSThread、OperationQueue、GCD)

在前面的博客中如果用到了异步请求的话,也是用到的第三方的东西,没有正儿八经的用过iOS中多线程的东西.其实多线程的东西还是蛮重要的,如果对于之前学过操作系统的小伙伴来说,理解多线程的东西还是比较容易的,今天就做一个小的demo来详细的了解一下iOS中的多线程的东西.可能下面的东西会比较枯燥,但还是比较实用的. 多线程用的还是比较多的,废话少说了,下面的两张截图是今天我们实验的最终结果,应该是比较全的,小伙伴们由图来分析具体的功能吧: 功能说明: 1.点击同步请求图片,观察整个UI界面的变化,并点

iOS边练边学--多线程介绍、NSThread的简单实用、线程安全以及线程之间的通信

一.iOS中的多线程 多线程的原理(之前多线程这块没好好学,之前对多线程的理解也是错误的,这里更正,好好学习这块) iOS中多线程的实现方案有以下几种 二.NSThread线程类的简单实用(直接上代码) 三.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题(存钱取钱的例子,多个售票员卖票的例子) 安全隐患解决的方法 --- 互斥锁(图解)

iOS 中实现多线程的方法 NSThread

使用多线程的好处: 1.   使用多线程可以把程序中占据时间长的任务放到后台去处理,如图片.视频的下载 2.   发挥多核处理器的优势,并发执行让系统运行的更快.更流程.用户体验更好 使用多线程的缺点: 1.   大量的线程降低代码的可读性 2.   更多的线程需要更多的内存空间 3.   当多个线程对一个资源出现争夺的时候要注意线程安全的问题 NSThread(两种创建方式) 优点:NSThread比其它两个轻量级 缺点:需要自己管理线程的生命周期,线程同步,线程同步时对数据的加锁会有一定的系

IOS多线程开发之NSThread

概要 使用NSThread的例子,线程创建.启动.线程同步.锁.线程的交互,需要注意的时线程的交互,因为IOS规定只有主线程能够修改UI,所以如果子线程要修改UI的话,需要与主线程交互,即调用方法- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;实现 示例代码 // // ViewController.m // NSThreadDemo // // Crea

iOS多线程篇:NSThread简单介绍和使用

一.什么是NSThread NSThread是基于线程使用,轻量级的多线程编程方法(相对GCD和NSOperation),一个NSThread对象代表一个线程, 需要手动管理线程的生命周期,处理线程同步等问题. 二.NSThread方法介绍 1)动态创建 1 NSThread * newThread = [[NSThread alloc]initWithTarget:self selector:@selector(threadRun) object:nil]; 动态方法返回一个新的thread对