多线程

线程的定义:

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

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

程序启动会默认开启一条线程,这条线程被称为主线程或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\n", (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\n", (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\n", (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\n", (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\n", (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\n", (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\n", (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\n", (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\n", (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开发中的锁
 

 

posted @ 2020-01-14 09:12  do+better  阅读(213)  评论(0编辑  收藏  举报