这些年我们用过的线程

进程

在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。一个进程至少要有一个线程,线程是进程的基本执行单元,进程的任务都是在线程中完成的。

多线程的优缺点

  • 优点:
    1.适当提高程序执行效率;
    2.适当提高资源利用率;
  • 缺点:
    1.开启线程需要占一定的内存空间(一般情况下主线程占用1M,子线程占用512KB),大量开启线程,将降低程序的性能;
    2.线程越多,CPU调度开销就越大;
    3.线程之前的通讯和数据共享越复杂;

实现方案

Pthread

1 特点:
(1)一套通用的多线程API
(2)适用于Unix\Linux\Windows等系统
(3)跨平台\可移植
(4)使用难度大
2 使用语言:c语言
3 使用频率:几乎不用
4 线程生命周期:由程序员进行管理

NSThread

1 特点:
(1)使用更加面向对象
(2)简单易用,可直接操作线程对象
2 使用语言:C语言
3 使用频率:偶尔使用
4 线程生命周期:由程序员进行管理
5 实现方案:线程的状态:新建、就绪、运行、阻塞、死亡

  • 初始化
//初始化   
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(timeRun:) object:@"123"];   
thread.name = @"YAN";   
// 设置优先级:(0.0~1.0)1.0最高优先级,默认为0.5   
thread.threadPriority = 1.0;   
// 开启线程    [thread start];
// 初始化:马上创建并开启新的线程   
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"yan"];
//初始化:隐式创建   
[self performSelectorInBackground:@selector(background:) withObject:@"创建"];
  • 阻塞/暂停
  • 获取该线程: [NSThread currentThread]
  • 获取主线程: [NSThread mainThread]
  • 退出该线程: [NSThread exit]; 不能在主线程中调用该方法;该线程死亡之后线程的代码将不在执行
GCD

1 特点:
(1)旨在替代NSThread等线程技术
(2)充分利用设备的多核(自动)
(3)并发数量、执行顺序是不可控的
(4)只支持队列的FIFO原则
2 使用语言:OC语言
3 使用频率:经常使用
4 线程生命周期:自动管理
5 底层实现:
a.基于XNU内核实现
b.属于libdispatch库
c.dispatchQueue:管理block,dispatchSource:处理事件
6 实现方案
同步执行:不具备开启新线程的能力,只能在当前线程执行任务;
异步执行:具备开启新线程的能力,在新开启的线程执行任务;
并发队列:多个任务同时执行;
串行队列:一个任务完成之后执行下一个任务;

  • 创建 Queues:
     dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr)//队列名称、队列属性(为null时为串行队列)

①串行队列:任务添加到队列中马上执行

     dispatch_queue_t serialQueue = dispatch_queue_create("yan.liu",DISPATCH_QUEUE_SERIAL);
     dispatch_queue_t mainQueue = dispatch_get_main_queue();//主队列为系统创建的特殊串行队列,刷新UI在主队列进行;

②并发队列:将所有任务添加到队列之后才执行

dispatch_queue_t concurrentQueue = dispatch_queue_create("yan.liu", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 系统创建的全局并发队列
DISPATCH_QUEUE_PRIORITY_HIGH 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台执行
  • 处理事件:

①异步串行:开启了一个子线程,按照任务的添加顺序,并且任务添加到队列中马上执行

dispatch_async(serialQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_async(serialQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

②同步串行:没有开启新的线程,在当前线程中进行,按照任务的添加顺序,并且任务添加到队列中马上执行

dispatch_sync(serialQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_sync(serialQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

③异步并行:开启新的线程,所有任务添加完之后执行

dispatch_async(concurrentQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_async(concurrentQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

④同步并行:没有开启新的线程,所有任务添加完成之后之后

dispatch_sync(concurrentQueue, ^{        NSLog(@"1-%@",[NSThread currentThread]);    });   
dispatch_sync(concurrentQueue, ^{        NSLog(@"2-%@",[NSThread currentThread]);    });

注意:主队列不能同步执行,会造成线程卡死

  • 其他方法
    1. 栅栏方法
    NSLog(@"start");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"1-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"2-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"3-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"4-%@",[NSThread currentThread]);
    });
    NSLog(@"end");
运行结果
  1. 使用dispatch_barrier_async
    NSLog(@"start");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"1-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"2-%@",[NSThread currentThread]);
    });
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"barrier-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"3-%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"4-%@",[NSThread currentThread]);
    });
    NSLog(@"end");
运行结果
  1. 延迟方法:dispatch_after 延迟5S
NSLog(@"start");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"end-%@",[NSThread currentThread]);
    });
运行结果
  1. 一次性函数:dispatch_once 常用于单例
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"执行一次");
    });
  1. 快速迭代方法:与for不同的是:在并行队列中同时遍历并且创建新的线程;在串行队列中和for类似,不会创建新的线程
    dispatch_apply(5, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t count) {
        NSLog(@"%zu - %@",count,[NSThread currentThread]);
    });
运行结果
  1. 组队列:
dispatch_group_wait(group, DISPATCH_TIME_FOREVER) 等待group执行完毕继续往下进行,会阻塞线程;
dispatch_group_notify 等待group执行完毕执行block,不会阻塞进程;
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, concurrentQueue, ^(size_t count) {
            NSLog(@"%zu -1- %@",count,[NSThread currentThread]);
        });
    });
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, serialQueue, ^(size_t count) {
            NSLog(@"%zu -2- %@",count,[NSThread currentThread]);
        });
    });
//    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"回到 - %@",[NSThread currentThread]);
    });
    NSLog(@"1234567");
运行结果
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, concurrentQueue, ^(size_t count) {
            NSLog(@"%zu -1- %@",count,[NSThread currentThread]);
        });
    });
    dispatch_group_async(group, globalQueue, ^{
        dispatch_apply(5, serialQueue, ^(size_t count) {
            NSLog(@"%zu -2- %@",count,[NSThread currentThread]);
        });
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        NSLog(@"回到 - %@",[NSThread currentThread]);
//    });
    NSLog(@"1234567");

运行结果

注意:dispatch_group_notify不能与 dispatch_async结合使用,否则会导致group任务没有执行完毕,就执行了dispatch_group_notify

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, globalQueue, ^{
        dispatch_async(concurrentQueue, ^{
            for (int i = 0; i < 3; i ++) {
                NSLog(@"%d -1- %@",i,[NSThread currentThread]);
            }
        });
    });
    dispatch_group_async(group, globalQueue, ^{
        dispatch_async(concurrentQueue, ^{
            for (int i = 0; i < 3; i ++) {
                sleep(1);
                NSLog(@"%d -2- %@",i,[NSThread currentThread]);
            }
        });
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"回到 - %@",[NSThread currentThread]);
    });
    NSLog(@"1234567");
运行结果

dispatch_group_enter(group); …… dispatch_group_leave(group);中间的任务由group管理,dispatch_group_enter(group)group任务加1,dispatch_group_leave(group)group任务减1;

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            sleep(1);
            NSLog(@"%d -1- %@",i,[NSThread currentThread]);
        }
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d -2- %@",i,[NSThread currentThread]);
        }
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"回到 - %@",[NSThread currentThread]);
    });
    NSLog(@"123456789");
运行结果
  1. 信号量:dispatch_semaphore

是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数,并继续执行下面的语句。

①创建信号量: dispatch_semaphore_t sema = dispatch_semaphore_create(0);
②发送信号: dispatch_semaphore_signal(sema) 信号量加1
③等待信号: dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER) 信号量减1

timeout:dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC) 5秒
DISPATCH_TIME_FOREVER 遥远的未来
DISPATCH_TIME_NOW 当前时间
在等待时间timeout期间没有获得信号量或者信号量一直为0,其所处的线程将继续执行下面的语句;

  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    __block long x = 0;
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1-%ld",x);
        sleep(2);
        x=dispatch_semaphore_signal(sema);
        NSLog(@"2-%ld",x);
    });
    NSLog(@"3-%ld",x);
    x=dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    NSLog(@"4-%ld",x);
运行结果
  1. dispatch_suspend(queue)挂起queue:不会挂起正在进行的任务,当前任务完成之后挂起之后的任务
  2. dispatch_resume(queue)恢复queue:恢复被挂起的queue
    注意:dispatch_suspend/dispatch_resume对于系统的queue不起作用;挂起 dispatch_sync中的queue会造成死锁;成对出现;
  3. dispatch_retain() 、dispatch_release() :ARC中并不需要自己管理
NSOperation

1 特点:
(1)基于GCD(底层是GCD)封装的API
(2)比GCD多了一些更简单实用的功能
(3)使用更加面向对象
(4)可以控制并发数量,调整执行顺序和操作之间的依赖关系
(5)执行速度没有GCD快
2 使用语言:OC语言
3 使用频率:经常使用
4 线程生命周期:自动管理
5 实现方案:

  • NSOperation:默认是同步执行
执行操作:start
取消操作: cancel
监听操作:设置在start之前
    [invocation setCompletionBlock:^{
        NSLog(@"执行完毕");
    }];
    invocation.completionBlock = ^{
        NSLog(@"执行完毕");
    };
  • NSInvocationOperation:在主线程中执行
    NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocation:) object:@"yan"];
    [invocation start];
    - (void)invocation:(id)obj{
         NSLog(@"%@--%@",obj,[NSThread currentThread]);
    }
  • NSBlockOperation:首先在主线程执行,如果主线程被占用就开启新的线程,多个任务时异步并发
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
    }];
    [blockOperation start];
  • NSOperationQueue

主队列: NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
其他队列: NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init]; 通过设置并发数确定队列是并发还是串行、异步

addOperationWithBlock:默认异步并发, maxConcurrentOperationCount默认为-1,即并发;设置为1时,otherQueue为串行队列,大于1位并行队列;

    otherQueue.maxConcurrentOperationCount = 1;
    [otherQueue addOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
运行结果

addOperation:
添加NSInvocationOperation:在新线程中执行;
添加NSBlockOperation:异步并发
[otherQueue cancelAllOperations];取消这句代码之前队列里添加的所有没有执行的任务;

    [otherQueue addOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [otherQueue cancelAllOperations];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
运行结果

[otherQueue setSuspended:YES];YES:暂停;NO:恢复
[invocation setQueuePriority:NSOperationQueuePriorityLow];
[blockOperation4 setQueuePriority:NSOperationQueuePriorityHigh]; 设置queue中的优先级,相对于同一个queue;对执行顺序不起绝对想作用;好像并没有起作用;

添加依赖:可以不同queue之间创建依赖,但是不能创建环形依赖;
依赖影响任务的执行顺序:

// 顺序随机
    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue addOperation:blockOperation3];
    [otherQueue addOperation:blockOperation4];
    [blockOperation1 addDependency:blockOperation4];
    [blockOperation4 addDependency:blockOperation2];

添加依赖后的运行结果

等待任务完成:等待单个任务完成:waitUntilFinished 阻塞线程(当前线程与主线程),直到任务完成;要写在任务添加到queue之后,否则阻塞线程;阻塞waitUntilFinished之后的命令;

    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue addOperation:blockOperation3];
    [blockOperation1 waitUntilFinished];
    [otherQueue addOperation:blockOperation4];
    NSLog(@"end--%@",[NSThread currentThread]);
运行结果

这样添加不阻塞主线程:

    NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];
    }];
    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        [blockOperation1 waitUntilFinished];
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4--%@",[NSThread currentThread]);
    }];
    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue addOperation:blockOperation3];
    [otherQueue addOperation:blockOperation4];
    NSLog(@"end--%@",[NSThread currentThread]);

运行结果

等待queue中的任务完成:阻塞线程(当前线程和主线程),直到queue添加的任务完成;在waitUntilAllOperationsAreFinished之后添加的任务不算在内;

    [otherQueue addOperation:blockOperation1];
    [otherQueue addOperation:blockOperation2];
    [otherQueue waitUntilAllOperationsAreFinished];
    [otherQueue addOperation:blockOperation3];
    [otherQueue addOperation:blockOperation4];
    NSLog(@"end--%@",[NSThread currentThread]);
运行结果

线程安全

多个线程访问同一块资源会产生线程安全问题;

  • 死锁:线程A和线程B各持有一把锁,线程A在等线程B释放锁,线程B在等线程A释放锁;两个线程都拿不到锁,也不能释放自己的锁,造成线程被阻塞在进程中。
  • 活锁:线程A可以使用资源,但是它让给了其他线程;线程B也可以使用资源,它也让给了其他线程先使用;最后导致两个线程都无法使用资源,导致活锁;线程没有被阻塞;
  • 解决方案:参考
    • 原子操作:最简单最基础的线程安全方法;一个线程对共享变量进行操作时,其他线程不能对该变量进行操作,并且其他线程不会被阻塞;
    • 内存屏障:帮助CPU规定操作指令的顺序,会降低编译器的性能
    • 可见变量:CPU为了提高处理速度,通常情况是先将主存中的数据读取到缓存中,进行的一些操作之后也不会立即写回主存;所以很可能会读取到旧值;但在共享变量加volatile关键字,声明为可见变量;CPU从缓存中读取并进行修改之后会将该变量立即存入到主存中,其他CPU操作时会先判断缓存中的数据是否过期,如果过期会从主存中重新缓存;会降低编译器的性能;
    • 单例:
    • 信号量(GCD):确保代码不会被并发调用
    • 加锁保护:阻塞线程,性能降低
    1. 互斥锁:互斥锁扮演的角色就是代码或者说任务的栅栏,它将你希望保护的代码片段围起来,当其他线程也试图执行这段代码时会被互斥锁阻塞,直到互斥锁被释放,如果多个线程同时竞争一个互斥锁,有且只有一个线程可以获得互斥锁。
    2. 递归锁:递归锁是互斥锁的变种。它允许一个线程在已经拥有一个锁,并且没有释放的前提下再次获得锁。当该线程释放锁时也需要一个一个释放。
    3. 读写锁:读写锁一般用在有资源被多个线程频繁的进行读操作,而只偶尔会有专职线程对该资源进行写操作的情况下。读写锁可被多个进行读操作的线程获得,但只能被一个进行写操作的线程获得,当有读操作的线程等待时,写操作的线程就不能获得锁,反之亦然,当写操作的线程在等待时,读操作的线程就不能获得锁。
    4. 分配锁:这种锁作用在进程级别,将进程保护起来,但是该锁不会阻塞其他进程,而是当其他进程与被保护进程交互时分配锁会告知前来的访问进程被访问进程处于锁状态,让前来访问的进程自行决定下一个操作。
    5. 自旋锁:自旋锁与互斥锁有点类似,但不同的是其他线程不会被自旋锁阻塞,而是而是在进程中空转,就是执行一个空的循环。一般用于自旋锁被持有时间较短的情况。
    6. 双检测锁:这种锁的目的是为了最大限度推迟上锁的时间,因为在多线程中线程安全对开销还是挺大的,所以一般能不上锁就不上锁。所以这种锁在上锁之前会先检查一次是否需要上锁,在上锁之后再检查一次,最后才真正执行操作。
    7. Conditions :一种多线程间协调通信的机制,用于标明共享资源是否可被访问或者确保一系列任务能按照指定的执行顺序执行。如果一个线程试图访问一个共享资源,而正在访问该资源的线程将其条件设置为不可访问,那么该线程会被阻塞,直到正在访问该资源的线程将访问条件更改为可访问状态或者说给被阻塞的线程发送信号后,被阻塞的线程才能正常访问这个资源。

如果使用锁机制,则不要使用volatile关键字;

  • 实现方案:
  • NSLock
    当一个线程进行访问的时候,该线程获得锁,其他线程进行访问的时候,将被操作系统挂起,直到该线程释放锁,其他线程才能对其进行访问
    tryLock:当前线程尝试视图获取锁,获取失败不会阻塞当前线程;
 NSLock *lock = [[NSLock alloc] init];   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"加锁%@",[NSThread currentThread]);
        sleep(3);
        NSLog(@"解锁%@",[NSThread currentThread]);
        [lock unlock];
    });
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock tryLock]) {
            NSLog(@"锁可用%@",[NSThread currentThread]);
            [lock unlock];
        }else{
            NSLog(@"锁不可用%@",[NSThread currentThread]);
        }
        NSLog(@"%@",[NSThread currentThread]);
    });
运行结果

[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4.0]]:在时间限制内获取锁,时间限制内获取失败会阻塞线程,直到获取成功;时间超时后不会阻塞线程;

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4.0]]) {
            NSLog(@"锁可用%@",[NSThread currentThread]);
            [lock unlock];
        }else{
            NSLog(@"锁不可用%@",[NSThread currentThread]);
        }
        NSLog(@"%@",[NSThread currentThread]);
    });
运行结果
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]) {
            NSLog(@"锁可用%@",[NSThread currentThread]);
            [lock unlock];
        }else{
            NSLog(@"锁不可用%@",[NSThread currentThread]);
        }
        NSLog(@"%@",[NSThread currentThread]);
    });
运行结果
  • NSRecursiveLock:可以被同一线程多次请求,也不会造成死锁;主要用于循环和递归
    以下递归函数如果使用NSLock会造成死锁,而使用NSRecursiveLock就不会
    NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^lockMethod)(int);
        lockMethod = ^(int count){
            [recursiveLock lock];
            if (count < 5) {
                NSLog(@"%d",count);
                lockMethod(count+1);
            }
            [recursiveLock unlock];
            NSLog(@"%d---%@",count,[NSThread currentThread]);
        };
        lockMethod(0);
    });

[recursiveLock tryLock]/ [recursiveLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]:同NSLock

  • NSConditionLock:可以设置加锁和释放锁的条件

[conditionLock lockWhenCondition:0]:condition为0 的时候加锁;
[conditionLock tryLockWhenCondition:1]:尝试获取condition为1的锁;不会阻塞线程;
[conditionLock unlockWithCondition:1]:解锁,并将condition设置为1;
[conditionLock lockWhenCondition:1 beforeDate:[NSDate dateWithTimeIntervalSinceNow:3]]:在时间限制内获取condition为1的锁;阻塞当前线程;

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [conditionLock lockWhenCondition:1];
        NSLog(@"条件加锁--1--%@",[NSThread currentThread]);
        NSLog(@"condition-1-%ld",conditionLock.condition);
        sleep(2);
        [conditionLock unlock];
        NSLog(@"解锁--1--%@",[NSThread currentThread]);
    });
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [conditionLock lockWhenCondition:0];
        NSLog(@"条件加锁--0--%@",[NSThread currentThread]);
        NSLog(@"condition-0-%ld",conditionLock.condition);
        sleep(2);
        [conditionLock unlockWithCondition:1];
        NSLog(@"解锁--0--%@",[NSThread currentThread]);
    });
运行结果
  • NSCondition:在给定线程中充当锁和检查点,会阻塞线程

[condition signal]:唤醒最先进入wait的线程
[condition broadcast]:换线所有进入wait状态的线程

    NSCondition *condition = [[NSCondition alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"加锁 --1--%@",[NSThread currentThread]);
        [condition wait];
        NSLog(@"wait-1-%@",[NSThread currentThread]);
        [condition unlock];
        NSLog(@"解锁 --wait1--%@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"加锁 --2--%@",[NSThread currentThread]);
        [condition wait];
        NSLog(@"wait-2-%@",[NSThread currentThread]);
        [condition unlock];
        NSLog(@"解锁 --wait2--%@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [condition lock];
        NSLog(@"加锁 ----%@",[NSThread currentThread]);
        [condition signal];
        NSLog(@"唤醒 ----%@",[NSThread currentThread]);
        [condition unlock];
        NSLog(@"解锁 ----%@",[NSThread currentThread]);
    });
运行结果
  • @synchronized:修饰变量(对象),确保变量的线程安全,它能自动为修饰的变量创建互斥锁或解锁
//    @property(nonatomic, assign) NSInteger count;
    static void (^myBlock)();
    myBlock = ^(){
        @synchronized(self){
            self.count += 1;// 如果不使用@synchronized,可能出现同时被修改的情况,即相同的值;
        };
    };
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        myBlock();
        NSLog(@"1--%ld--%@",self.count,[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        myBlock();
        NSLog(@"2--%ld--%@",self.count,[NSThread currentThread]);
    });
运行结果

线程通信

一个线程传递数据给另一个线程;一个线程执行完特定任务之后,执行另一个线程;

  • NSThread

[self performSelectorOnMainThread:@selector(run:) withObject:@"主线程" waitUntilDone:YES];   
[self performSelector:@selector(run:) onThread:thread withObject:@"指定线程" waitUntilDone:YES];   
[self performSelector:@selector(run:) withObject:@"当前线程"];
  • GCD

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"其他操作");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"进行UI刷新操作");
        });
    });
  • NSOperation

    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];
    [otherQueue addOperationWithBlock:^{
        NSLog(@"一些操作%@",[NSThread currentThread]);
        [mainQueue addOperationWithBlock:^{
            NSLog(@"刷新UI%@",[NSThread currentThread]);
        }];
    }];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容