IOS开发-多线程开发之线程安全篇

前言:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件和同一个方法等。因此当多个线程访问同一块资源时,很容易会发生数据错误及数据不安全等问题。因此要避免这些问题,我们需要使用“线程锁”来实现。

本文主要论述IOS创建锁的方法(总结):

一、使用关键字

1)@synchronized(互斥锁)

优点:使用@synchronized关键字可以很方便地创建锁对象,而且不用显式的创建锁对象。

缺点:会隐式添加一个异常处理来保护代码,该异常处理会在异常抛出的时候自动释放互斥锁。而这种隐式的异常处理会带来系统的额外开销,为优化资源,你可以使用锁对象。

二、Object-C”语言

1)NSLock(互斥锁)

2)NSRecursiveLock(递归锁)

条件锁,递归或循环方法时使用此方法实现锁,可避免死锁等问题。

3)NSConditionLock(条件锁)

使用此方法可以指定,只有满足条件的时候才可以解锁。

4)NSDistributedLock(分布式锁)

在IOS中不需要用到,也没有这个方法,因此本文不作介绍,这里写出来只是想让大家知道有这个锁存在。

如果想要学习NSDistributedLock的话,你可以创建MAC OS的项目自己演练,方法请自行Google,谢谢。

三、C语言

1)pthread_mutex_t(互斥锁)

2)GCD-信号量(“互斥锁”)

3)pthread_cond_t(条件锁)

线程安全 —— 锁

一、使用关键字:

1)@synchronized

// 实例类person
Person *person = [[Person alloc] init];

// 线程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(person) {
        [person personA];
        [NSThread sleepForTimeInterval:3]; // 线程休眠3秒
    }
});

// 线程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(person) {
        [person personB];
    }
});

关键字@synchronized的使用,锁定的对象为锁的唯一标识,只有标识相同时,才满足互斥。如果线程B锁对象person改为self或其它标识,那么线程B将不会被阻塞。你是否看到@synchronized(self) ,也是对的。它可以锁任何对象,描述为@synchronized(anObj)。

二、Object-C语言

1)使用NSLock实现锁

// 实例类person
Person *person = [[Person alloc] init];
// 创建锁
NSLock *myLock = [[NSLock alloc] init];

// 线程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [myLock lock];
    [person personA];
    [NSThread sleepForTimeInterval:5];
    [myLock unlock];
});

// 线程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [myLock lock];
    [person personB];
    [myLock unlock];
}); 

程序运行结果:线程B会等待线程A解锁后,才会去执行线程B。如果线程B把lock和unlock方法去掉之后,则线程B不会被阻塞,这个和synchronized的一样,需要使用同样的锁对象才会互斥。

NSLock类还提供tryLock方法,意思是尝试锁定,当锁定失败时,不会阻塞进程,而是会返回NO。你也可以使用lockBeforeDate:方法,意思是在指定时间之前尝试锁定,如果在指定时间前都不能锁定,也是会返回NO。

注意:锁定(lock)和解锁(unLock)必须配对使用

2)使用NSRecursiveLock类实现锁

// 实例类person
Person *person = [[Person alloc] init];
// 创建锁对象
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];

// 创建递归方法
static void (^testCode)(int);
testCode = ^(int value) {
    [theLock tryLock];
    if (value > 0)
    {
        [person personA];
        [NSThread sleepForTimeInterval:1];
        testCode(value - 1);
    }
    [theLock unlock];
};

//线程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    testCode(5);
});

//线程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [theLock lock];
    [person personB];
    [theLock unlock];
});

如果我们把NSRecursiveLock类换成NSLock类,那么程序就会死锁。因为在此例子中,递归方法会造成锁被多次锁定(Lock),所以自己也被阻塞了。而使用NSRecursiveLock类,则可以避免这个问题。

3)使用NSConditionLock(条件锁)类实现锁:

使用此方法可以创建一个指定开锁的条件,只有满足条件,才能开锁。

// 实例类person
Person *person = [[Person alloc] init];
// 创建条件锁
NSConditionLock *conditionLock = [[NSConditionLock alloc] init];

// 线程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lock];
    [person personA];
    [NSThread sleepForTimeInterval:5];
    [conditionLock unlockWithCondition:10];
});

// 线程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lockWhenCondition:10];
    [person personB];
    [conditionLock unlock];
});

线程A使用的是lock方法,因此会直接进行锁定,并且指定了只有满足10的情况下,才能成功解锁。

unlockWithCondition:方法,创建条件锁,参数传入“整型”。lockWhenCondition:方法,则为解锁,也是传入一个“整型”的参数。

三、C语言

1)使用pthread_mutex_t实现锁

注意:必须在头文件导入:#import <pthread.h>

// 实例类person
Person *person = [[Person alloc] init];

// 创建锁对象
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

// 线程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personA];
    [NSThread sleepForTimeInterval:5];
    pthread_mutex_unlock(&mutex);
});

// 线程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personB];
    pthread_mutex_unlock(&mutex);
});

实现效果和上例的相一致

2)使用GCD实现“锁”(信号量)

GCD提供一种信号的机制,使用它我们可以创建“锁”(信号量和锁是有区别的,具体请自行百度)。

// 实例类person
Person *person = [[Person alloc] init];

// 创建并设置信量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

// 线程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [person personA];
    [NSThread sleepForTimeInterval:5];
    dispatch_semaphore_signal(semaphore);
});

// 线程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [person personB];
    dispatch_semaphore_signal(semaphore);
});

效果也是和上例介绍的相一致。

我在这里解释一下代码。dispatch_semaphore_wait方法是把信号量加1,dispatch_semaphore_signal是把信号量减1。

我们把信号量当作是一个计数器,当计数器是一个非负整数时,所有通过它的线程都应该把这个整数减1。如果计数器大于0,那么则允许访问,并把计数器减1。如果为0,则访问被禁止,所有通过它的线程都处于等待的状态。

3)使用POSIX(条件锁)创建锁

// 实例类person
Person *person = [[Person alloc] init];

// 创建互斥锁
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
// 创建条件锁
__block pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

// 线程A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    pthread_cond_wait(&cond, &mutex);
    [person personA];
    pthread_mutex_unlock(&mutex);
});

// 线程B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personB];
    [NSThread sleepForTimeInterval:5];
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
});

效果:程序会首先调用线程B,在5秒后再调用线程A。因为在线程A中创建了等待条件锁,线程B有激活锁,只有当线程B执行完后会激活线程A。

pthread_cond_wait方法为等待条件锁。

pthread_cond_signal方法为激动一个相同条件的条件锁。

简单总结:

一般来说,如果项目不大,我们都会偷点懒,直接使用关键字@synchronized建立锁,懒人方法。其次可以使用苹果提供的OC方法,最后才会去使用C去建立锁。

本文参考文章:

iOS多线程开发(四)---线程同步

Objective-C中不同方式实现锁(一)

信号量与互斥锁



博文作者:GarveyCalvin

博文出处:http://www.cnblogs.com/GarveyCalvin/

本文版权归作者和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作!

时间: 2024-10-07 18:07:51

IOS开发-多线程开发之线程安全篇的相关文章

iOS学习——多线程开发(NSThread)

首先我们得知道,cpu仅能同时处理一条线程,多线程并发并不是多条线程同时进行,而是cpu不断在线程间切换进行,所以线程并不是越多越好,当存在大量线程,会让cpu在切换间疲于奔命,反而不利于开发. 具体来说,iOS方面多线程也就是两种,pthread以及NSThread.pthread是C语言写的多线程,好处是不仅仅用在iOS移动端开发,基本上支持C语言的都可以使用,缺点就是C语言的共性了,不易识别,难记而且并不支持arc.所以在iOS中多线程开发还是去了解NSThread更好,pthread掌握

Java并发/多线程系列——线程安全篇(1)

创建和启动Java线程 Java线程是个对象,和其他任何的Java对象一样.线程是类的实例java.lang.Thread,或该类的子类的实例.除了对象之外,java线程还可以执行代码. 创建和启动线程 在Java中创建一个线程是这样完成的: Thread thread = new Thread(); 要启动Java线程,您将调用其start()方法,如下所示: thread.start(); 此示例不指定要执行的线程的任何代码.启动后,线程将立即停止. 有两种方法来指定线程应该执行什么代码.第

iOS开发多线程篇—线程的状态

iOS开发多线程篇—线程的状态 一.简单介绍 线程的创建: self.thread=[[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil]; 说明:创建线程有多种方式,这里不做过多的介绍. 线程的开启: [self.thread start]; 线程的运行和阻塞: (1)设置线程阻塞1,阻塞2秒 [NSThread sleepForTimeInterval:2.0]; (2)第二种设置线程阻塞2,以当前时

iOS开发多线程之自定义NSOperation

iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 1 // 2 // YYViewController.h 3 // 01-自定义Operation 4 // 5 // Created by apple on 14-6-26. 6 // Copyright (c) 2014年 itcase. All rig

iOS开发多线程篇—自定义NSOperation

iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 1 // 2 // YYViewController.h 3 // 01-自定义Operation 4 // 5 // Created by apple on 14-6-26. 6 // Copyright (c) 2014年 itcase. All rig

iOS开发多线程篇 09 —NSOperation简单介绍

iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作?:配合使用NSOperation和NSOperationQueue也能实现多线程编程 NSOperation和NSOperationQueue实现多线程的具体步骤: (1)先将需要执行的操作封装到一个NSOperation对象中 (2)然后将NSOperation对象添加到NSOperationQueue中 (3)系统会?动将NSOperationQueue中的NSOpe

iOS开发多线程篇 05 —GCD介绍

iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 GCD是苹果公司为多核的并行运算提出的解决方案 GCD会自动利用更多的CPU内核(比如双核.四核) GCD会自动管理线程的生命周期(创建线程.调度任务.销毁线程) 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码 3.提示 (1)GCD存在于libdispatch.dylib这个库中

iOS开发多线程篇—多线程简单介绍

iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过“活动监视器”可以查看Mac系统中所开启的进程 2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行 3.线程

iOS开发多线程篇—多线程的的相关概念(1)

iOS开发多线程篇-多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过"活动监视器"可以查看Mac系统中所开启的进程 2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行