多线程

作者: 勇往直前888 | 来源:发表于2015-11-16 16:58 被阅读90次

    NSThread

    这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 NSThread.currentThread(),它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。
    在做定时器的时候也可以用用,只是用多线程启动,并不需要关心线程本身。

    创建并启动

    • 先创建线程类,再启动
      let thread = NSThread(target: self, selector: "run:", object: nil)
    
      //启动
      thread.start()```
      
    * 创建并自动启动
    

    NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil)

    * 使用 NSObject 的方法创建并自动启动
    

    self.performSelectorInBackground("run:", withObject: nil)

    >Swift中,这个方法不推荐使用。
    
    ##其他方法
    

    //取消线程
    public func cancel()

    //启动线程
    public func start()

    //判断某个线程的状态的属性
    public var executing: Bool { get }
    public var finished: Bool { get }
    public var cancelled: Bool { get }

    //设置和获取线程名字
    public var name: String?

    //获取当前线程信息
    public class func currentThread() -> NSThread

    //获取主线程信息
    public class func mainThread() -> NSThread

    //使当前线程暂停一段时间,或者暂停到某个时刻
    public class func sleepForTimeInterval(ti: NSTimeInterval)
    public class func sleepUntilDate(date: NSDate)

    
    #NSOperation和NSOperationQueue
    ```NSOperation``` 是苹果公司对 ```GCD``` 的封装,完全面向对象。操作步骤:
    1. 将要执行的任务封装到一个 ```NSOperation``` 对象中
    。
    2. 将此任务添加到一个 ```NSOperationQueue``` 对象中
    。
    
    ##普通用法
    

    //1.创建队列
    let queue = NSOperationQueue()

    //2.创建NSBlockOperation对象
    let operation = NSBlockOperation { () -> Void in
    NSLog("%@", NSThread.currentThread())
    }

    //3.添加多个Block
    for i in 0..<5 {
    operation.addExecutionBlock { () -> Void in
    NSLog("第%ld次 - %@", i, NSThread.currentThread())
    }
    }

    //4.队列添加任务
    queue.addOperation(operation)```

    多线程环境下,打印输出用NSLog(),不用print()

    任务依赖

    比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:

    //1.任务一:下载图片
    let operation1 = NSBlockOperation { () -> Void in
        NSLog("下载图片 - %@", NSThread.currentThread())
        NSThread.sleepForTimeInterval(1.0)
    }
    
    //2.任务二:打水印
    let operation2 = NSBlockOperation { () -> Void in
        NSLog("打水印   - %@", NSThread.currentThread())
        NSThread.sleepForTimeInterval(1.0)
    }
    
    //3.任务三:上传图片
    let operation3 = NSBlockOperation { () -> Void in
        NSLog("上传图片 - %@", NSThread.currentThread())
        NSThread.sleepForTimeInterval(1.0)
    }
    
    //4.设置依赖
    operation2.addDependency(operation1)    //任务二依赖任务一
    operation3.addDependency(operation2)    //任务三依赖任务二
    
    //5.创建队列并加入任务
    let queue = NSOperationQueue()
    queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)
    

    从其他线程回到主线程

    NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in
    
    }```
    
    #GCD
    它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。
    
    ##普通用法
    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {

            //这里写需要大量时间的代码
            print("这里写需要大量时间的代码")
            
            dispatch_async(dispatch_get_main_queue(), {
                
                //这里返回主线程,写需要主线程执行的代码
                print("这里返回主线程,写需要主线程执行的代码")
            })
        }) 
    
    
    ## 创建队列
    * 主队列
    
    

    let queue = ispatch_get_main_queue()

    * 自己创建的队列
    
    

    //串行队列
    let queue = dispatch_queue_create("tk.bourne.testQueue", nil);
    let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL)
    //并行队列
    let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT)

    * 全局并行队列
    
    

    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

    >只要是并行任务一般都加入到这个队列。这是系统提供的一个并发队列。
    
    ##创建任务
    * 同步任务:会阻塞当前线程 (SYNC)
    
    

    dispatch_sync(<#queue#>, { () -> Void in
    //code here
    println(NSThread.currentThread())
    })

    异步任务:不会阻塞当前线程 (ASYNC)
    

    dispatch_async(<#queue#>, { () -> Void in
    //code here
    println(NSThread.currentThread())
    })

    ###示例一:
    以下代码在主线程调用,结果是什么?
    

    NSLog("之前 - %@", NSThread.currentThread())
    dispatch_sync(dispatch_get_main_queue(), { () -> Void in
    NSLog("sync - %@", NSThread.currentThread())
    })
    NSLog("之后 - %@", NSThread.currentThread())

    ####答案:
    只会打印第一句:```之前 - <NSThread: 0x7fb3a9e16470>{number = 1, name = main}``` ,然后主线程就卡死了,你可以在界面上放一个按钮,你就会发现点不了了。
    ####解释:
    同步任务会阻塞当前线程,然后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。
    那么这里的步骤就是:打印完第一句后,```dispatch_sync``` 立即阻塞当前的主线程,然后把 Block 中的任务放到 ```main_queue``` 中,可是 ```main_queue``` 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,```dispatch_sync``` 就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。
    
    ###示例二:
    以下代码会产生什么结果?
    

    let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL)

    NSLog("之前 - %@", NSThread.currentThread())

    dispatch_async(queue, { () -> Void in
        NSLog("sync之前 - %@", NSThread.currentThread())
        dispatch_sync(queue, { () -> Void in
             NSLog("sync - %@", NSThread.currentThread())
        })
        NSLog("sync之后 - %@", NSThread.currentThread())
    

    })

    NSLog("之后 - %@", NSThread.currentThread())

    ####答案:
    ```2015-07-30 02:06:51.058 test[33329:8793087] 之前 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}
    2015-07-30 02:06:51.059 test[33329:8793356] sync之前 - <NSThread: 0x7fe32062e9f0>{number = 2, name = (null)}
    2015-07-30 02:06:51.059 test[33329:8793087] 之后 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}```
    很明显 ```sync - %@``` 和 ```sync之后 - %@``` 没有被打印出来!这是为什么呢?我们再来分析一下:
    ####分析:
    
    1. 使用 DISPATCH_QUEUE_SERIAL 这个参数,创建了一个 串行队列。
    
    2. 打印出 ```之前 - %@ ```这句。
    
    3. ```dispatch_async``` 异步执行,所以当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 ```之后 - %@```这句, 另一条执行 Block 中的内容打印 ```sync之前 - %@``` 这句。因为这两条是并行的,所以打印的先后顺序无所谓。
    
    4. ```dispatch_sync```同步执行,于是它所在的线程会被阻塞,一直等到 ```sync``` 里的任务执行完才会继续往下。于是 ```sync``` 就把自己 Block 中的任务放到 ```queue``` 中,可 ```queue``` 是一个串行队列,一次执行一个任务,所以 ```sync``` 的 Block 必须等到前一个任务执行完毕,可是 ```queue``` 正在执行的任务就是被 ```sync``` 阻塞了的那个。于是又发生了死锁。所以 ```sync``` 所在的线程被卡死了。剩下的两句代码自然不会打印。
    
    ## 队列组
    队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。下面是使用方法,这是一个很实用的功能。
    

    //1.创建队列组
    let group = dispatch_group_create()
    //2.创建队列
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

    //3.多次使用队列组的方法执行任务, 只有异步方法
    //3.1.执行3次循环
    dispatch_group_async(group, queue) { () -> Void in
    for _ in 0..<3 {
    NSLog("group-01 - %@", NSThread.currentThread())
    }
    }

    //3.2.主队列执行8次循环
    dispatch_group_async(group, dispatch_get_main_queue()) { () -> Void in
    for _ in 0..<8 {
    NSLog("group-02 - %@", NSThread.currentThread())
    }
    }

    //3.3.执行5次循环
    dispatch_group_async(group, queue) { () -> Void in
    for _ in 0..<5 {
    NSLog("group-03 - %@", NSThread.currentThread())
    }
    }

    //4.都完成后会自动通知
    dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
    NSLog("完成 - %@", NSThread.currentThread())
    }

    
    ##从其他线程回到主线程
    

    dispatch_async(dispatch_get_main_queue(), { () -> Void in

    })```

    延时执行

    var time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(10 * NSEC_PER_SEC))
    dispatch_after(time, globalQueue) { () -> Void in
        println("在10秒后执行")
    } 
    

    NSEC_PER_SEC表示的是秒数,它还提供了NSEC_PER_MSEC表示毫秒。

    参考文献

    一篇比较好的关于多线程的文章

    相关文章

      网友评论

        本文标题:多线程

        本文链接:https://www.haomeiwen.com/subject/eruhhttx.html