iOS 多线程同步,线程加锁

思考1、项目中怎么确保数据安全

我们在写代码的时候可能很少人会去特别注意这些细节的东西,在项目中我碰到过好几次这样的事情。
1、大家应该都知道数据库句柄只能存在一个,当出现两个句柄操作的时候,数据库会崩溃报错。那这里我们就需要对句柄操作加锁。有人会问为什么会出现两个句柄,数据处理肯定是放到异步线程去处理的。那当我们的业务量比较大的时候,很多业务可能都在操作数据库,不加锁处理当然会有两个句柄了~
2、蓝牙数据组装问题,蓝牙数据我们一般会和硬件工程师约定一个指定的传输数据结构,header+playload+CRC校验等等。当多个蓝牙指令同时操作的时候,不同的header信息传进来的playload 可能会有混淆,蓝牙操作肯定也是放在异步线程中处理的。这个时候数据肯定不是绝对安全的。但是加锁可以完美解决这个问题
3、创建一个常量或者是一个对象的时候,我们都会用@property (nonatomic, xxx), nonatomic肯定不是线程安全的,一般UI控件肯定不需要考虑安全性,UI操作都是放在主线程里面的。

以上只是一些我个人碰到的一些问题,实际上肯定还有更多的问题

信号量的作用:一个APP代表一个进程,一个进程里面可以有多个线程。 信号量可以控制线程的个数!为什么要控制线程的个数,当我们开辟多个线程的时候其实是对当前APP的负担,是不是一定上会消耗手机的电量。CPU需要协调多个线程之间的关系,频繁的切换线程。CPU主要处理算法、数据处理等操作,GPU主要处理图片渲染。一个优质的APP当然是少耗电少发热。

- (void)handle {
    
//    NSInteger groupIndex = 0;
    //信号量加锁的创建 这里1的意思是只允许一个执行操作
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    // dispatch_group 用于监控县城执行完成
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    for (int i =0; i< 10; i++) {
        // dispatch_group_enter和dispatch_group_leave 一定是成对出现的
        dispatch_group_enter(group);//使分组里正要执行的任务数递增
        dispatch_async(queue, ^{
            //dispatch_semaphore_wait和dispatch_semaphore_signal 一定是成对出现的 要不然要出现线程卡死。
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"currentThread===%@ i=%d", [NSThread currentThread], i);
            dispatch_semaphore_signal(semaphore);
            dispatch_group_leave(group);//使分组里的任务数递减
        });
    }
    
//    __block NSInteger groupIndex = 0;
    // 当group分组里面的任务数成为0的时候m,里面会有一个唤醒机制。 执行dispatch_group_notify 这个方法
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       
        NSLog(@"\n---------------执行完毕可进行自己需要的操作了");
//        NSLog(@"currentThread===%@ groupIndex=%ld", [NSThread currentThread], groupIndex);
    });
}

执行结果如下:我们可以看到虽然这是一个10个循环的异步线程,按道理打印i的值应该是随机的。但是我们加锁了异步线程的打印信息。相当于当前只能有一个线程能执行NSLog这句代码,所以顺序执行了打印信息。 最后我们加了一个dispatch_group_notify,

实际项目中可能有这样的需求:在执行多个网络请求,最后需要输出什么东西,我们可以在dispatch_group_notify执行

2020-04-13 10:03:39.217616+0800 demo1[3326:1375671] currentThread==={number = 4, name = (null)} i=0
2020-04-13 10:03:39.217860+0800 demo1[3326:1375669] currentThread==={number = 5, name = (null)} i=1
2020-04-13 10:03:39.217996+0800 demo1[3326:1375670] currentThread==={number = 3, name = (null)} i=2
2020-04-13 10:03:39.218063+0800 demo1[3326:1375671] currentThread==={number = 4, name = (null)} i=3
2020-04-13 10:03:39.218157+0800 demo1[3326:1375672] currentThread==={number = 6, name = (null)} i=4
2020-04-13 10:03:39.218461+0800 demo1[3326:1375674] currentThread==={number = 7, name = (null)} i=5
2020-04-13 10:03:39.218540+0800 demo1[3326:1375675] currentThread==={number = 8, name = (null)} i=6
2020-04-13 10:03:39.218609+0800 demo1[3326:1375676] currentThread==={number = 9, name = (null)} i=7
2020-04-13 10:03:39.218659+0800 demo1[3326:1375669] currentThread==={number = 5, name = (null)} i=8
2020-04-13 10:03:39.218716+0800 demo1[3326:1375677] currentThread==={number = 10, name = (null)} i=9
2020-04-13 10:03:39.228002+0800 demo1[3326:1375507] 
---------------执行完毕可进行自己需要的操作了

有人会问dispatch_semaphore_t这里起的是加锁功能,那为什么不用NSLock~,当然不一样了,NSLock只是锁,它只保证原子性,并不会控制线程的数量。

下面的代码我们不加锁和加锁的区别、 groupIndex是递增的,

for (int i =0; i< 10; i++) {
        // dispatch_group_enter和dispatch_group_leave 一定是成对出现的
        dispatch_group_enter(group);//使分组里正要执行的任务数递增
        dispatch_async(queue, ^{
            //dispatch_semaphore_wait和dispatch_semaphore_signal 一定是成对出现的 要不然要出现线程卡死。
//            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//            @synchronized (self) {
            
//            [_lock lock];
            groupIndex ++;
            NSLog(@"currentThread===%@ i=%d groupIndex=%ld", [NSThread currentThread], i, groupIndex);
//            }
//            [_lock unlock];
            
            
//            dispatch_semaphore_signal(semaphore);
            dispatch_group_leave(group);//使分组里的任务数递减
        });
    }

这个是不加锁的打印,可以看到groupIndex的打印是乱序的~

2020-04-13 10:45:03.165289+0800 demo1[3355:1384983] currentThread==={number = 5, name = (null)} i=0 groupIndex=1
2020-04-13 10:45:03.165395+0800 demo1[3355:1384983] currentThread==={number = 5, name = (null)} i=2 groupIndex=3
2020-04-13 10:45:03.165508+0800 demo1[3355:1384982] currentThread==={number = 6, name = (null)} i=1 groupIndex=2
2020-04-13 10:45:03.165628+0800 demo1[3355:1384987] currentThread==={number = 8, name = (null)} i=6 groupIndex=7
2020-04-13 10:45:03.165668+0800 demo1[3355:1384984] currentThread==={number = 4, name = (null)} i=3 groupIndex=4
2020-04-13 10:45:03.165711+0800 demo1[3355:1384983] currentThread==={number = 5, name = (null)} i=4 groupIndex=5
2020-04-13 10:45:03.165706+0800 demo1[3355:1384981] currentThread==={number = 7, name = (null)} i=5 groupIndex=6
2020-04-13 10:45:03.165706+0800 demo1[3355:1384982] currentThread==={number = 6, name = (null)} i=8 groupIndex=9
2020-04-13 10:45:03.165685+0800 demo1[3355:1384986] currentThread==={number = 3, name = (null)} i=7 groupIndex=8
2020-04-13 10:45:03.165704+0800 demo1[3355:1384987] currentThread==={number = 8, name = (null)} i=9 groupIndex=10
2020-04-13 10:45:03.175206+0800 demo1[3355:1384960] 
---------------执行完毕可进行自己需要的操作了

下面是加锁的打印,可以看到groupIndex的打印是顺序的~ 实际项目中实际使用需要自己摸索!

2020-04-13 10:48:35.494058+0800 demo1[3358:1385835] currentThread==={number = 3, name = (null)} i=0 groupIndex=1
2020-04-13 10:48:35.494180+0800 demo1[3358:1385835] currentThread==={number = 3, name = (null)} i=2 groupIndex=2
2020-04-13 10:48:35.494260+0800 demo1[3358:1385835] currentThread==={number = 3, name = (null)} i=5 groupIndex=3
2020-04-13 10:48:35.494331+0800 demo1[3358:1385835] currentThread==={number = 3, name = (null)} i=6 groupIndex=4
2020-04-13 10:48:35.494469+0800 demo1[3358:1385835] currentThread==={number = 3, name = (null)} i=9 groupIndex=5
2020-04-13 10:48:35.494542+0800 demo1[3358:1385836] currentThread==={number = 5, name = (null)} i=4 groupIndex=6
2020-04-13 10:48:35.494673+0800 demo1[3358:1385837] currentThread==={number = 4, name = (null)} i=3 groupIndex=7
2020-04-13 10:48:35.494887+0800 demo1[3358:1385834] currentThread==={number = 6, name = (null)} i=1 groupIndex=8
2020-04-13 10:48:35.494953+0800 demo1[3358:1385840] currentThread==={number = 7, name = (null)} i=8 groupIndex=9
2020-04-13 10:48:35.495035+0800 demo1[3358:1385839] currentThread==={number = 8, name = (null)} i=7 groupIndex=10
2020-04-13 10:48:35.506153+0800 demo1[3358:1385811] 
---------------执行完毕可进行自己需要的操作了

思考2、为什么本身OC自带排序算法,面试的时候还是会问我们数据结构和算法

现在我们都是站在巨人的肩膀上在完善或者说是实现个人定制化需求,但是真正厉害的其实还是底层,多了解一些底层总是可以让我们对这份职业有一份敬畏,也能加强开阔我们的逻辑思维。
下面是用Java写的归并排序算法,归并算法的平均时间是nlogn,相比于快排,堆排序更稳定,数据量越大相对比来说,使用快速排序这一类比冒泡,选择优势更明显

package com.kclTest;

import java.util.Arrays;

public class sortDemo {

    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        int[] temp = new int[arr.length];
        sort(arr, 0, arr.length-1, temp);
    }
     // 使用递归的方式去拆分数组 重组数组,理解上会比较麻烦
    private static void sort(int[] arr, int left, int right, int[] temp) {

        if (left < right) {
            int mid = (left+right)/2;
            sort(arr, left, mid, temp);
            sort(arr, mid+1, right, temp);
            merge(arr, left, mid, right, temp);
        }
    }

    private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        System.out.println("left="+left);
        int i = left;
        int j = mid+1;
        int t = 0;
        while (i<=mid && j<=right) {

            if (arr[i] <= arr[j]) {
                temp[t++] = arr[i++];
            } else {
                temp[t++] = arr[j++];
            }
        }

        while (i<=mid) {
            temp[t++] = arr[i++];
        }
        while (j<=right) {
            temp[t++] = arr[j++];
        }

        t = 0;
        while (left <= right) {
            arr[left++] = temp[t++];
        }
    }
}

冒泡排序/选择排序

public static void  maopaoSort(int[] arr) {
        for (int i = 0; i< arr.length; i++) {
            for(int j = 0; jarr[j+1]) {
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }

    public static void xuanzeSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = i+1; j < arr.length; j++) {
                if (arr[i] < arr[j]) {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }

思考3:Java有抽象类的概念,那么iOS怎么实现抽象类,或者是有哪个系统类是抽象类

抽象类的概念其实就是:本身是不能实例化的,只有子类能实例化,那么我们又会想了为什么要有这样一个概念。抽象类就是一个父类也像一个基类,基类定义了一些共同特性,该基类不能在任何地方初始化。子类继承于父类,定制化自己的特有属性或者方法,一定程度上达到代码解耦的效果! 抽象类实用:工厂设计模式Masonry,不懂的可以去百度看看.可以去看看Masonry的设计模式,里面用到了工厂模式+链式编程模式。

NSOperation UIGestureRecognizer MASConstraint 都是抽象类 可以具体看一下MASConstraint

模拟Java 实现抽象类

#import "AbstractObject.h"

#define AbstractMethodNotImplemented() \
[NSException exceptionWithName:NSInternalInconsistencyException \
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
userInfo:nil]

@implementation AbstractObject

- (instancetype)init {
    // 断言判断 如果是本类初始化 崩溃信息: AbstractObject is an abstract class
    NSAssert(![self isMemberOfClass:[AbstractObject class]], @"AbstractObject is an abstract class");
    return [super init];
}
// 必须要实现的抽象方法
- (void)abstractMustImplementMethod {
    
    AbstractMethodNotImplemented();
    
}

思考4 iOS开发要怎么突破自我

搞了4-5年开发,难道就真的成为一个代码搬运工, 方向:内存性能优化,电量优化,架构师方向,转型,全栈,

你可能感兴趣的:(iOS 多线程同步,线程加锁)