iOS:多线程NSThread的详细使用

NSThread具体使用:直接继承NSObject


NSThread:.
优点:NSThread 是轻量级的,使用简单
缺点:需要自己管理线程的生命周期、线程同步。线程同步对数据的加锁会有一定的系统开销
 
1、属性
           @property (readonly, retain) NSMutableDictionary *threadDictionary;  //线程字典
           @property double threadPriority;                                                  //线程优先级
           @property NSQualityOfService qualityOfService;                           //服务质量
           @property (copy) NSString *name;                                              //线程名字
           @property NSUInteger stackSize;                                               //堆栈大小
           @property (readonly) BOOL isMainThread;                                   //是否是主线程
           @property (readonly, getter=isExecuting) BOOL executing ;              //线程是否正在执行
           @property (readonly, getter=isFinished) BOOL finished;                 //线程是否执行完毕
           @property (readonly, getter=isCancelled) BOOL cancelled ;               //线程是否取消
 
2、通知
           将成为多线程的通知
            FOUNDATION_EXPORT NSString * const NSWillBecomeMultiThreadedNotification; 
           已经成为单线程的通知
           FOUNDATION_EXPORT NSString * const NSDidBecomeSingleThreadedNotification;
           线程将要退出的通知
           FOUNDATION_EXPORT NSString * const NSThreadWillExitNotification;
 
3、主要方法    
              ※当前线程
             + (NSThread *)currentThread;
             ※创建线程的类方法,并添加执行事件

             + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

             ※是否是多线程

             + (BOOL)isMultiThreaded;

             ※定时休眠

             + (void)sleepUntilDate:(NSDate *)date;

             ※休眠时间

             + (void)sleepForTimeInterval:(NSTimeInterval)ti;

             ※退出线程

            + (void)exit;

            ※线程优先级

           + (double)threadPriority;

           ※设置线程优先级

           + (BOOL)setThreadPriority:(double)p;

           ※显示当前栈内容(返回的是这个线程在栈中所占的地址所组成的数组)

           + (NSArray *)callStackReturnAddresses;

           ※返回栈空间的符号

           + (NSArray *)callStackSymbols;

           ※是否是主线程

           + (BOOL)isMainThread; 

           ※创建线程的实例方法,并添加执行事件

           - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;

           ※取消线程

           - (void)cancel;

           ※启动线程

           - (void)start;

           ※线程主体要执行的方法 :thread body method

           - (void)main;

 
4、类别:@interface NSObject (NSThreadPerformAdditions)    

           ※调用主线程,传入一个与主线程RunLoop循环执行有关的数组

           - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes: (NSArray *)array;

           ※调用主线程更新UI

           - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

           ※调用指定的线程,传入一个与线程RunLoop循环执行有关的数组

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

           ※调用指定的线程更新数据

           - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone: (BOOL)wait;

            ※在后台调用线程

           - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

 

具体举例如下:多线程卖票(要求:把当前票数和当前线程的名字显示在文本视图上)

1.在控件工具中拖拽一个文本视图控件并与类相关联

@property (weak, nonatomic) IBOutlet UITextView *textView;  //拖拽一个文本视图控件并与类关联

2.准备数据,并将文本视图中默认的内容置空

    //准备资源
    _tickets = 20;
    
    //将默认文本视图中的内容置空
    self.textView.text = @"";
    
    //取消自动布局,用来控制文本视图的滚动
    self.textView.layoutManager.allowsNonContiguousLayout = NO;

3.创建两个线程买票

    //创建两个线程并启动线程
    //售票线程-1
    NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    //设置线程的名字,以区别不同的线程
    [thread1 setName:@"售票线程-1"];
    //启动线程
    [thread1 start];
    
    
    //售票线程-2
    NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    //设置线程的名字,以区别不同的线程
    [thread2 setName:@"售票线程-2"];
    //启动线程
    [thread2 start];

4.为主线程创建一个方法,专门用来更新并显示UI的

#pragma mark -更新UI

-(void)appendTextView:(NSString *)text
{
    //1.获取textView中已有的内容
    NSMutableString *stringM = [[NSMutableString alloc]initWithString:self.textView.text];
    
    
    //2.追加新的内容
    [stringM appendString:[NSString stringWithFormat:@"\n%@",text]];
    [self.textView setText:stringM];
    
    //3.调节焦点,滚动视图
    NSRange range = NSMakeRange(stringM.length, 1);
    [self.textView scrollRangeToVisible:range];
}

5.为买票线程创建一个方法,负责买票

#pragma mark -卖票的过程

在方法-(void)saleTicket

        {

              while (YES) {

                    执行代码如下:

              }

         }

//当票数大于0时,即if(_tickets>0)时,

操作数据并更新UI为:

//并发执行时,采用同步代码块加锁,防止数据在一个线程的操作过程中被另一个线程抢占篡改,锁必须是唯一的
@synchronized(self)
{
  //拼接字符串(当前数据信息)
NSString *str = [NSString stringWithFormat:@"当前票数:%d 当前线程:%@",_tickets,[[NSThread currentThread]name ]]; //调用主线程,更新UI [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES]; _tickets--; }

进行休眠为:

//休眠一段时间,模拟耗时操作
if([[[NSThread currentThread]name] isEqualToString:@"售票线程-1"])
{
     [NSThread sleepForTimeInterval:0.3f];
}
else
{
     [NSThread sleepForTimeInterval:0.2f];
}

//当票数小于或等于0时,更新UI,并结束线程

//拼接字符串(当前数据信息)
NSString *str = [NSString stringWithFormat:@"票已经卖完 当前线程:%@",[[NSThread currentThread]name]];
            
//调用主线程,更新UI
[self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];
            
break; //结束while死循环,结束线程

演示结果如下:

当然,上面采用的是同步代码块隐式处理线程同步的问题,还可以采用显式加锁的方式来防止数据同步。

首先,声明一个NSLock的锁对象

@property (strong,nonatomic)NSLock *lock;

然后将上面的@synchronized(self)代码做修改,如下:

//在访问竞争资源前加锁
[self.lock lock];
            
//拼接字符串(当前数据信息)
NSString *str = [NSString stringWithFormat:@"当前票数:%d 当前线程:%@",_tickets,[[NSThread currentThread]name]];
            
//调用主线程,更新UI
[self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];            
_tickets--;
            
//访问完竞争资源马上解锁
[self.lock unlock];

演示结果相同:

 

 

 

 

 

 

 

posted @ 2015-10-06 20:35  XYQ全哥  阅读(497)  评论(0编辑  收藏  举报