多线程
多线程
  pthread
  NSThread
    简介
      NSThread是基于线程使用，轻量级的多线程编程方法（相对GCD和NSOperation），一个NSThread对象代表一个线程，
需要手动管理线程的生命周期，处理线程同步等问题。
    方法介绍
      动态创建，需要手动启动
        NSThread *newThread = [[NSThread alloc]initWithTarget:self
selector:@selector(threadRun) object:nil];
[newThread start];
      静态创建
        [NSThread detachNewThreadSelector:@selector(threadTestRun) toTarget:self withObject:nil];
      获取线程
        [NSThread currentThread]; 获取当前线程
[NSThread mainThread]; 获取主线程
      线程暂停
        NSThread的暂停会有阻塞当前线程的效果
[NSThread sleepForTimeInterval:1.0];　（以暂停一秒为例）
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
      线程取消
        取消线程并不会马上停止并退出线程，仅仅只作（线程是否需要退出）状态记录
[newThread cancel];
      线程停止
        停止方法会立即终止除主线程以外所有线程（无论是否在执行任务）并退出，需要在掌控所有线程状态的情况下调用此方法，
否则可能会导致内存问题。
[NSThread exit];
      状态查询
        - (BOOL)isExecuting；是否正在执行
- (BOOL)isFinished；是否完成
- (BOOL)isCancelled；是否取消
      线程优先级设置
        [newThread setQualityOfService:NSQualityOfServiceUserInitiated];
      线程间通信
        当前线程执行操作
          [self performSelector:@selector(threadRun)];
[self performSelector:@selector(threadRun) withObject:nil];
[self performSelector:@selector(threadRun) withObject:nil afterDelay:2.0];
        主线程执行操作
          [self performSelectorOnMainThread:@selector(threadRun) withObject:nil
waitUntilDone:YES];
waitUntilDone为YES时，threadRun执行完毕后才执行其后主线程的代码逻辑
waitUntilDone为NO时，threadRun在其后主线程代码执行完毕后才执行
        其他线程执行操作
          [self performSelector:@selector(threadRun) onThread:newThread
withObject:nil waitUntilDone:YES];
//这里指定为某个线程
[self performSelectorInBackground:@selector(threadRun) withObject:nil];
//这里指定为后台线程
    结合runloop实现常驻线程，参考runloop
  GCD
    简介
      Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。
* GCD 可用于多核的并行运算
* GCD 会自动利用更多的 CPU 内核（比如双核、四核）
* GCD 会自动管理线程的生命周期（创建线程、调度任务、销毁线程）
* 程序员只需要告诉 GCD 想要执行什么任务，不需要编写任何线程管理代码
    任务和队列
      任务
        同步执行（sync）
          同步添加任务到指定的队列中，在添加的任务执行结束之前，会一直等待，直到队列里面的任务完成之后再继续执行。
          只能在当前线程中执行任务，不具备开启新线程的能力。
        异步执行（async）
          异步添加任务到指定的队列中，它不会做任何等待，可以继续执行任务。
          可以在新的线程中执行任务，具备开启新线程的能力。但是并不一定开启新线程。这跟任务所指定的队列类型有关。
      队列
        串行队列
          每次只有一个任务被执行。让任务一个接着一个地执行。（只开启一个线程，一个任务执行完毕后，再执行下一个任务）
        并行队列
          可以让多个任务并发（同时）执行。（可以开启多个线程，并且同时执行任务），并发队列 的并发功能只有在异步（dispatch_async）函数下才有效
      使用步骤
        创建一个队列（串行队列或并发队列）
          普通队列
            可以使用dispatch_queue_create来创建队列，需要传入两个参数，第一个参数表示队列的唯一标识符，用于 DEBUG，可为空，Dispatch Queue 的名称推荐使用应用程序 ID 这种逆序全程域名；第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列，DISPATCH_QUEUE_CONCURRENT 表示并发队列。
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
          主队列
            所有放在主队列中的任务，都会放到主线程中执行。
可使用dispatch_get_main_queue()获得主队列。
          全局并发队列
            可以使用dispatch_get_global_queue来获取。需要传入两个参数。第一个参数表示队列优先级，一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用，用0即可。
        将任务追加到任务的等待队列中，
然后系统就会根据任务类型执行任务（同步执行或异步执行）
          同步执行 + 并发队列
            没有开启新线程，串行执行任务
          异步执行 + 并发队列
            有开启新线程，并发执行任务
          同步执行 + 串行队列
            当前串行队列下
              互相等待，死锁崩溃
            其他队列下
              没有开启新线程，串行执行任务
          异步执行 + 串行队列
            有开启新线程(1条)，串行执行任务
          同步执行 + 主队列
            主线程
              互相等待，死锁崩溃
            其他线程
              没有开启新线程，串行执行任务
          异步执行 + 主队列
            没有开启新线程，串行执行任务
    其他方法
      dispatch_barrier_async
        先执行完栅栏前面的操作，再执行栅栏操作，最后再执行栅栏后边的操作。
      dispatch_after
        延时执行
      dispatch_once
        一次性代码
      dispatch_apply
        快速迭代方法
      dispatch_group
        队列组
      dispatch_semaphore
        信号量
  NSOperation
    简介
      NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装，完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
优势：
1.可添加完成的代码块，在操作完成后执行。
2.添加操作之间的依赖关系，方便的控制执行顺序。
3.设定操作执行的优先级。
4.可以很方便的取消一个操作的执行。
5.使用 KVO 观察对操作执行状态的更改：isExecuteing、isFinished、isCancelled。
    操作和队列
      操作
        执行操作的意思，换句话说就是你在线程中执行的那段代码。
在 GCD 中是放在 block 中的。在 NSOperation 中，我们使用 NSOperation 子类 NSInvocationOperation、NSBlockOperation，或者自定义子类来封装操作。
      队列
        这里的队列指操作队列，即用来存放操作的队列。不同于 GCD 中的调度队列 FIFO（先进先出）的原则。NSOperationQueue 对于添加到队列中的操作，首先进入准备就绪的状态（就绪状态取决于操作之间的依赖关系），然后进入就绪状态的操作的开始执行顺序（非结束执行顺序）由操作之间相对的优先级决定（优先级是操作对象自身的属性）。
操作队列通过设置最大并发操作数（maxConcurrentOperationCount）来控制并发、串行。
NSOperationQueue 为我们提供了两种不同类型的队列：主队列和自定义队列。主队列运行在主线程之上，而自定义队列在后台执行。
      使用步骤
        创建操作：先将需要执行的操作封装到一个 NSOperation 对象中。
在不使用 NSOperationQueue，单独使用 NSOperation 的情况下系统同步执行操作
          NSInvocationOperation
            // 1.创建 NSInvocationOperation 对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
// 2.调用 start 方法开始执行操作
[op start];
// 在当前线程执行的，并没有开启新线程。
          NSBlockOperation
            // 1.创建 NSBlockOperation 对象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// 2.添加额外的操作
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// 3.调用 start 方法开始执行操作
[op start];
          自定义继承自 NSOperation 的子类，
通过实现内部相应的方法来封装操作。
            可以通过重写 main 或者 start 方法 来定义自己的 NSOperation 对象。
重写main方法比较简单，我们不需要管理操作的状态属性 isExecuting 和 isFinished。当 main 执行完返回的时候，这个操作就结束了。因为main方法执行完就认为operation结束了，所以一般可以用来执行同步任务。
如果你希望拥有更多的控制权，或者想在一个操作中可以执行异步任务，那么就重写start方法, 但是注意：这种情况下，你必须手动管理操作的状态， 只有当发送 isFinished的 KVO 消息时，才认为是 operation 结束。
当实现了start方法时，默认会执行start方法，而不执行main方法。为了让操作队列能够捕获到操作的改变，需要将状态的属性以配合 KVO的方式进行实现。如果你不使用它们默认的 setter 来进行设置的话你就需要在合适的时候发送合适的 KVO消息。
        创建队列：创建 NSOperationQueue 对象。
          主队列
[NSOperationQueue mainQueue];
          自定义队列（非主队列）
[[NSOperationQueue alloc] init];
        将操作加入到队列中：将 NSOperation 对象添加到 NSOperationQueue 对象中。系统就会自动将 NSOperationQueue 中的 NSOperation 取出来，在新线程中执行操作。
          - (void)addOperation:(NSOperation *)op;
          - (void)addOperationWithBlock:(void (^)(void))block;
    串行、并发
      maxConcurrentOperationCount（最大并发操作数）
控制的不是并发线程的数量，而是一个队列中同时能并发执行的最大操作数。
        -1(默认)，表示不进行限制，可进行并发执行。
        1，队列为串行队列，只能串行执行。
        >1，队列为并发队列，最大为系统设定的默认最大值
    操作依赖
      添加依赖
        - (void)addDependency:(NSOperation *)op;
      移除依赖
        - (void)removeDependency:(NSOperation *)op;
      获取依赖
        @property (readonly, copy) NSArray<NSOperation *> *dependencies;
    优先级
      就绪状态的操作
        当一个操作的所有依赖都已经完成时，操作对象通常会进入准备就绪状态，等待执行。
        没有需要依赖的操作，就是处于准备就绪状态的操作。
      queuePriority 属性决定了进入准备就绪状态下的操作之间的开始执行顺序。并且，优先级不能取代依赖关系。
    线程间的通信
      // 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 进行一些 UI 刷新等操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
    常用属性和方法
      NSOperation
        - (void)waitUntilFinished; 阻塞当前线程，直到该操作结束。可用于线程执行顺序的同步。
        - (void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。
      NSOperationQueue
        - (void)cancelAllOperations; 可以取消队列的所有操作。
        - (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态，NO 为恢复状态。
        - (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复，YES 代表暂停队列，NO 代表恢复队列。
        - (void)waitUntilAllOperationsAreFinished; 阻塞当前线程，直到队列中的操作全部执行完毕。
        - (void)cancelAllOperations; 可以取消队列的所有操作。
  锁
    NSLock
    synchronized（互斥锁）
    atomic
    dispatch_semaphore
    NSCondition
    NSConditionLock（条件锁）
    NSRecursiveLock（递归锁）
    自旋锁（OSSpinLock）
    pthread_mutex（互斥锁）
qualityOfService的枚举值如下：
NSQualityOfServiceUserInteractive：最高优先级，用于用户交互事件
NSQualityOfServiceUserInitiated：次高优先级，用于用户需要马上执行的事件
NSQualityOfServiceDefault：默认优先级，主线程和没有设置优先级的线程都默认为这个优先级
NSQualityOfServiceUtility：普通优先级，用于普通任务
NSQualityOfServiceBackground：最低优先级，用于不重要的任务
任务就是执行操作的意思，换句话说就是你在线程中执行的那段代码。
在 GCD 中是放在 block 中的。执行任务有两种方式：同步执行（sync）和异步执行（async）。
两者的主要区别是：是否等待队列的任务执行结束，以及是否具备开启新线程的能力。
这里的队列指执行任务的等待队列，即用来存放任务的队列。队列是一种特殊的线性表，采用 FIFO（先进先出）的原则，即新任务总是被插入到队列的末尾，而读取任务的时候总是从队列的头部开始读取。每读取一个任务，则从队列中释放一个任务。队列的结构可参考下图：
在 GCD 中有两种队列：串行队列和并发队列。两者都符合 FIFO（先进先出）的原则。两者的主要区别是：执行顺序不同，以及开启线程数不同。
通常我们会用 for 循环遍历，但是 GCD 给我们提供了快速迭代的函数dispatch_apply。dispatch_apply按照指定的次数将指定的任务追加到指定的队列中，并等待全部队列执行结束。
/**
* 快速迭代方法 dispatch_apply
*/
- (void)apply {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
}
* 调用队列组的 dispatch_group_async 先把任务放到队列中，然后将队列放入队列组中。或者使用队列组的 dispatch_group_enter、dispatch_group_leave 组合 来实现
dispatch_group_async。
- (void)group1 {
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1---%@",[NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2---%@",[NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"group---end");
});
}
- (void)group2 {
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_enter(dispatchGroup);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1---%@",[NSThread currentThread]);
dispatch_group_leave(dispatchGroup);
});
dispatch_group_enter(dispatchGroup);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2---%@",[NSThread currentThread]);
dispatch_group_leave(dispatchGroup);
});
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"group---end");
});
}
* 调用队列组的 dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait 回到当前线程继续向下执行（会阻塞当前线程）。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
当NSBlockOperation对象只封装一个对象的时候，任务在当前线程执行，如果封装多个操作，就会开启多线程，线程的条数有系统决定,操作也可能会在主线程调用
锁的性能比较：
NSLock 遵循 NSLocking 协议，lock 方法是加锁，unlock 是解锁，tryLock 是尝试加锁,不会阻塞线程，如果失败的话返回 NO，lockBeforeDate: 是在指定Date之前尝试加锁,会阻塞线程，如果在指定时间之前都不能加锁，则返回NO。
某个线程A调用lock方法。这样，NSLock将被上锁。可以执行“关键部分”，完成后，调用unlock方法。
如果，在线程A 调用unlock方法之前，另一个线程B调用了同一锁对象的lock方法。那么，线程B只有等待。直到线程A调用了unlock。
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程1-1");
[lock lock];
NSLog(@"线程1-2");
sleep(2);
NSLog(@"线程1-3");
[lock unlock];
NSLog(@"线程1-4");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程2-1");
sleep(1);//以保证让线程2的代码后执行
NSLog(@"线程2-2");
[lock lock];
NSLog(@"线程2-3");
[lock unlock];
NSLog(@"线程2-4");
});
//线程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程3-1");
sleep(1);//以保证让线程4的代码后执行
NSLog(@"线程3-2");
if ([lock tryLock]) {
NSLog(@"线程3-3");
[lock unlock];
}
else {
NSLog(@"线程3加锁失败");
}
});
//线程4
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程4-1");
sleep(1);//以保证让线程4的代码后执行
NSLog(@"线程4-2");
if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:3]]) {
NSLog(@"线程4-3");
[lock unlock];
}
else {
NSLog(@"线程4加锁失败");
}
});
1. synchronized会创建一个异常捕获handler和一些内部的锁，所以使用@synchronized替换普通锁的代价是要付出更多的时间消耗
2. 创建给@synchronized指令的对象是一个用来区别保护块的唯一标识符。如果你在两个不同的线程里面执行上述方法，每次在一个线程传递了一个不同的对象给anObj参数，那么每次都将会拥有它的锁，并持续处理，中间不会被其他线程阻塞。然而如果你传递的是同一个对象，那么多个线程中的一个线程会首先获得该锁，而其他线程将会被阻塞直到第一个线程完成它的临界区
3. 作为一个预防措施。@synchronized块隐式的添加一个异常处理例程来保护代码，该处理例程会在异常抛出的时候自动的释放互斥锁，这就意味着为了使用@synchronized指令，你必须在你的代码中启用异常处理。如果你不想让隐式的异常处理例程带来额外的开销，那么可以使用其他的锁
atomic只是给成员变量的set和get方法加了一个锁，防止多线程一直去读写这个成员变量。但这也仅仅是对读写的锁定，并不是线程安全。而且使用atomic比nonatomic慢了将近20倍
dispatch_semaphore 是 GCD 用来同步的一种方式，与他相关的只有三个函数，一个是创建信号量，一个是等待信号，一个是发送信号。
dispatch_semaphore_create(long value);
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore_signal(dispatch_semaphore_t dsema);
dispatch_semaphore 和 NSCondition 类似，都是一种基于信号的同步方式，但 NSCondition 信号只能发送，不能保存（如果没有线程在等待，则发送的信号会失效）。而 dispatch_semaphore 能保存发送的信号。dispatch_semaphore 的核心是 dispatch_semaphore_t 类型的信号量。
dispatch_semaphore_create(1) 方法可以创建一个 dispatch_semaphore_t 类型的信号量，设定信号量的初始值为 1。注意，这里的传入的参数必须大于或等于 0，否则 dispatch_semaphore_create 会返回 NULL。
dispatch_semaphore_wait(signal, overTime); 方法会判断 signal 的信号值是否大于 0。大于 0 不会阻塞线程，消耗掉一个信号，执行后续任务。如果信号值为 0，该线程会和 NSCondition 一样直接进入 waiting 状态，等待其他线程发送信号唤醒线程去执行后续任务，或者当 overTime 时限到了，也会执行后续任务。
dispatch_semaphore_signal(signal); 发送信号，如果没有等待的线程接受信号，则使 signal 信号值加一（做到对信号的保存）。
从上面的实例代码可以看到，一个 dispatch_semaphore_wait(signal, overTime); 方法会去对应一个 dispatch_semaphore_signal(signal); 看起来像 NSLock 的 lock 和 unlock，其实可以这样理解，区别只在于有信号量这个参数，lock unlock 只能同一时间，一个线程访问被保护的临界区，而如果 dispatch_semaphore 的信号量初始值为 x ，则可以有 x 个线程同时访问被保护的临界区。
dispatch_semaphore_t signal = dispatch_semaphore_create(1);
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(signal, overTime);
sleep(2);
NSLog(@"线程1");
dispatch_semaphore_signal(signal);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(signal, overTime);
NSLog(@"线程2");
dispatch_semaphore_signal(signal);
});
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait;//挂起线程
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal; //任意通知一个线程
- (void)broadcast; //通知所有等待的线程
@property (nullable,copy) NSString *nameNS_AVAILABLE(10_5,2_0);
@end
类似GCD的信号量，wait之后当前线程会被阻塞直到 lock signal。
在用的时候注意，首先对lock对象进行lock.
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
//初始化一个NSConditionLock对象
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition; //锁的条件
//满足条件时加锁
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
//如果接收对象的condition与给定的condition相等，则尝试获取锁，不阻塞线程
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
//解锁后，重置锁的条件
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
//在指定时间前尝试获取锁，若成功则返回YES 否则返回NO
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
递归锁，它允许同一线程多次加锁，而不会造成死锁。NSLock会造成死锁。
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
// NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^block)(int);
block = ^(int value) {
//加锁
[lock lock];
if (value > 0) {
NSLog(@"%d",value);
sleep(2);
//递归调用
block(--value);
}
//解锁
[lock unlock];
};
//调用代码块
block(10);
});
自旋锁不会引起调用者睡眠，如果自旋锁已经被别的执行单元保持，调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁，其作用是为了解决某项资源的互斥使用。
自旋锁的缺点：
1. 自旋锁一直占用CPU，他在未获得锁的情况下，一直运行－－自旋，所以占用着CPU，如果不能在很短的时 间内获得锁，这无疑会使CPU效率降低。
2. 在用自旋锁时有可能造成死锁，当递归调用时有可能造成死锁，调用有些其他函数也可能造成死锁，如 copy_to_user()、copy_from_user()、kmalloc()等。
在多线程的使用中，我们可以使用互斥锁来使多个线程保持同步。互斥锁会使等待的线程阻塞进入休眠状态，以使其他线程可以工作，当当前线程解锁时，会主动去将其他线程激活，这个过程包含了上下文的切换，cpu抢占，信号发送等开销，很明显，互斥锁的起始开销有些大，效率低于自旋锁。
互斥锁的使用过程中，主要有`pthread_mutex_init`，`pthread_mutex_destory`，`pthread_mutex_lock`，`pthread_mutex_unlock`这几个函数以完成锁的初始化，锁的销毁，上锁和释放锁操作。
1.声明一个互斥锁
```
pthread_mutex_t _lock;
//初始化
pthread_mutex_init(&_lock, NULL);
```
2.上锁 解锁
```
//上锁
pthread_mutex_lock(&_lock)
//这里加入可能产生死锁的操作 比如像文件写入 读取之类
//解锁
pthread_mutex_unlock(&_lock)
```
3.释放锁
`pthread_mutex_destroy(&_lock)`