• 多线程


    线程的定义:

    线程是进程的基本执行单元,一个进程的所有任务都在线程中执行

    进程要想执行任务,必须得有线程,进程至少要有一条线程

    程序启动会默认开启一条线程,这条线程被称为主线程或UI线程

    进程的定义 :

    进程是指在系统中正在运行的一个应用程序

    每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存

    进程与线程的关系:

    地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
    资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。

    一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
    进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
    执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
    线程是处理器调度的基本单位,但是进程不是。(cpu调度)

    多线程的意义: 解决一些在主线程执行的耗时操作,从而提升用户体验

    * 优点
      * 能适当提高程序的执行效率
      * 能适当提高资源的利用率(CPU,内存)
      * 线程上的任务执行完成后,线程会自动销毁
    * 缺点
       * 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占 512 KB)
       * 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
       * 线程越多,CPU 在调用线程上的开销就越大
       * 程序设计更加复杂,比如线程间的通信、多线程的数据共享

    多线程的原理:cpu调度切换执行线程

    创建线程的消耗

     

    主线程开辟空间512k,文档截图那里有点问题。

    NSThread:

    pthread_t:

     线程生命周期:

     

     线程池:

    线程池:

    饱和策略相关内容参考:博客

    线程安全: 两个线程同时操作一个属性对象会导致一张票卖两次的情况

    使用 @synchronized(self){} 互斥锁(递归锁,非递归锁重复访问资源会崩溃)修饰后可以避免这种情况

    锁还有很多种:

       {

            // 不再安全的 OSSpinLock  除此dispatch_semaphore 和 pthread_mutex 性能是最高的

            OSSpinLock lock = OS_SPINLOCK_INIT;

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                OSSpinLockLock(&lock);

                OSSpinLockUnlock(&lock);

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypeOSSpinLock] += end - begin;

            printf("OSSpinLock:               %8.2f ms ", (end - begin) * 1000);

        }

     

     

       {

            // 同步 ---

            // > 0

            // GCD 控制并发数

            

            

            dispatch_semaphore_t lock =  dispatch_semaphore_create(2); 控制并发数,如果设置1就是同步执行,只允许1条线程运行,相当于避免了多线程资源抢夺

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);

                

                dispatch_semaphore_signal(lock);

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypedispatch_semaphore] += end - begin;

            printf("dispatch_semaphore:       %8.2f ms ", (end - begin) * 1000);

        }

     

     

        {

            pthread_mutex_t lock;

            pthread_mutex_init(&lock, NULL);

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                pthread_mutex_lock(&lock);

                pthread_mutex_unlock(&lock);

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypepthread_mutex] += end - begin;

            pthread_mutex_destroy(&lock);

            printf("pthread_mutex:            %8.2f ms ", (end - begin) * 1000);

        }

        

        

        {条件锁

            NSCondition *lock = [NSCondition new];

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                [lock lock];

                [lock unlock];

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypeNSCondition] += end - begin;

            printf("NSCondition:              %8.2f ms ", (end - begin) * 1000);

        }

        

        

        {

            NSLock *lock = [NSLock new];

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                [lock lock];

                [lock unlock];

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypeNSLock] += end - begin;

            printf("NSLock:                   %8.2f ms ", (end - begin) * 1000);

        }

        

        

        {

            pthread_mutex_t lock;

            pthread_mutexattr_t attr;

            pthread_mutexattr_init(&attr);

            pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

            pthread_mutex_init(&lock, &attr);

            pthread_mutexattr_destroy(&attr);

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                pthread_mutex_lock(&lock);

                pthread_mutex_unlock(&lock);

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypepthread_mutex_recursive] += end - begin;

            pthread_mutex_destroy(&lock);

            printf("pthread_mutex(recursive): %8.2f ms ", (end - begin) * 1000);

        }

        

        

        {

            NSRecursiveLock *lock = [NSRecursiveLock new];

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                [lock lock];

                [lock unlock];

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypeNSRecursiveLock] += end - begin;

            printf("NSRecursiveLock:          %8.2f ms ", (end - begin) * 1000);

        }

        

        

        {

            NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:1];

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                [lock lock];

                [lock unlock];

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypeNSConditionLock] += end - begin;

            printf("NSConditionLock:          %8.2f ms ", (end - begin) * 1000);

        }

        

        

        {

            NSObject *lock = [NSObject new];

            begin = CACurrentMediaTime();

            for (int i = 0; i < count; i++) {

                @synchronized(lock) {}

            }

            end = CACurrentMediaTime();

            TimeCosts[LockTypesynchronized] += end - begin;

            printf("@synchronized:            %8.2f ms ", (end - begin) * 1000);

        }

     

    各个锁的性能:

    第一推荐使用 dispatch_semaphore 信号量

    第二推荐 NSLock 轻便,用的最多的

    第三推荐 @synchronized 语法简单,但是性能是做弱的

    atomic 与 nonatomic 就是典型的自旋锁

    自旋锁:忙等,一直询问是否有在执行的线程。

    互斥锁:如果有正在执行的线程,就休眠等待。

    读写锁:只允许一条线程写,但是允许多条线程读。

     互斥锁:允许多条线程访问,但是写的话只允许一天线程写入

    创建线程后,runloop不调用run是否不调用线程执行,runloop 常驻线程,可以保证线程不退出,runloop底层存储在一个全局字典中,dict[key(线程指针),runloop],线程的执行和runloop没有关系,runloop的执行是建立在线程上的,线程的start和调度和runloop没有关系,runloop无法对线程进行直接操作,runloop可以管理线程中执行的任务。

    线程执行完成后会等一段时间,看是否有需要执行的任务,如果没有就会被线程池回收,然后销毁,或者占用。(一般线程)

    timer的执行是依赖于runloop的,比较特殊。

    自旋锁与互斥锁的区别

    从实现原理上来讲,互斥锁属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和 Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞 (blocking),Core0 会在此时进行上下文切换(Context Switch)将线程A置于等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待。而Spin lock则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在 Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。

    自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是 否该自旋锁的保持者已经释放了锁。

    总结
      自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。
      互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作。直到被锁资源释放锁。此时会唤醒休眠线程。

    优缺点
      自旋锁的优点在于,因为自旋锁不会引起调用者睡眠,所以不会进行线程调度,cpu时间片轮转等耗时操作。所有如果能在很短的时间内获得锁,自旋锁的效率远高于互斥锁。
      缺点在于,自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时 间内获得锁,这无疑会使CPU效率降低。自旋锁不能实现递归调用。

    pthread_mutex 表示互斥锁。互斥锁可以传入不同参数,实现递归锁pthread_mutex(recursive)。NSLock,NSCondition,NSRecursiveLock,NSConditionLock都是内部封装的pthread_mutex,即都属于互斥锁。@synchronized是NSLock的一种封装,牺牲了效率,简洁了语法。

    OSSpinLock 表示自旋锁,从上图可以看到自旋锁的效率最高,但是现在的iOS因为优先级反转的问题,已经不安全,所以推荐使用pthread_mutex或者dispatch_semaphore。

    具体各种锁的内部实现等详情,参考: 深入理解iOS开发中的锁
     
  • 相关阅读:
    23. Sum Root to Leaf Numbers
    22. Surrounded Regions
    21. Clone Graph
    19. Palindrome Partitioning && Palindrome Partitioning II (回文分割)
    18. Word Ladder && Word Ladder II
    14. Reverse Linked List II
    20. Candy && Gas Station
    16. Copy List with Random Pointer
    ubuntu 下建立桌面快捷方式
    java基础篇-jar打包
  • 原文地址:https://www.cnblogs.com/coolcold/p/12190304.html
Copyright © 2020-2023  润新知