• 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:@"
    %@",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];

    演示结果相同:

  • 相关阅读:
    linux 安装nginx
    docker 安装redis
    c# socket初学小demo
    .NET 集成 swagger
    docker 常用命令
    docker 安装rabbitmq
    gitee为啥提交用户显示为多个,如何删除掉不要的账户,将所有的代码提交记录都记在一个账户里
    .NET 过滤器
    .NET设计文件系统
    LaTeX Workshop 配置信息
  • 原文地址:https://www.cnblogs.com/XYQ-208910/p/4857689.html
Copyright © 2020-2023  润新知