iOS中的锁

  在多线程操作过程中,往往一个数据同时被多个线程读写,在这种情况下,如果没有相应的机制对数据进行保护,就很可能会发生数据污染的的问题,给程序造成各种难以重现的潜在bug。


多线程安全中相关术语及概念(假设操作的是数据库):

1)脏读

  指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中。这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

(2)不可重复读

指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

(3)幻觉读

指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如:

目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。此时,事务B插入一条工资也为5000的记录。这时,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。


多线程的安全问题

线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。

线程安全:简单来说就是多个线程同时对共享资源进行访问时,采用了加锁机制,当一个线程访问共享资源,对该资源进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。

iOS多线程中的“锁”

1、互斥锁:@synchronized(id anObject)

-(void)myMethod:(id)anObj

{

@synchronized(anObj)

{

//do something here

}

}

2、atomic

OC在定义属性时有nonatomic和atomic两种选择。

atomic:原子属性,为setter方法加锁(默认就是atomic)。

nonatomic:非原子属性,不会为setter方法加锁。

atomic加锁原理:

@property(assign,atomic)intage;

-(void)setAge:(int)age

{

@synchronized(self){

_age=age;

}

}

3、NSLock

NSLock对象实现了NSLocking protocol,包含几个方法:

lock——加锁

unlock——解锁

tryLock——尝试加锁,如果失败了,并不会阻塞线程,只是立即返回NO

lockBeforeDate:——在指定的date之前暂时阻塞线程(如果没有获取锁的话),如果到期还没有获取锁,则线程被唤醒,函数立即返回NO。

比如:

NSLock *theLock=[[NSLockalloc]init];

if([theLocklock])

{

//do something here

[theLockunlock];

}

4、递归锁:NSRecursiveLock

多次调用不会阻塞已获取该锁的线程。

NSRecursiveLock *rcsLock=[[NSRecursiveLockalloc]init];

voidrecursiveLockTest(intvalue)

{

[rcsLocklock];

if(value!=0)

{

--value;

recursiveLockTest(value);

}

[rcsLockunlock];

}

recursiveLockTest(5);

上面如果直接使用NSLock就会造成死锁。NSRecursiveLock类定义的锁可以在同一线程多次lock,而不会造成死锁。递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操作。只有所有的锁住和解锁操作都平衡的时候,锁才真正被释放给其他线程获得。

5、条件锁:NSConditionLock

有时一把只会lock和unlock的锁未必就能完全满足我们的使用。因为普通的锁只能关心锁与不锁,而不在乎用什么钥匙才能开锁,而我们在处理资源共享的时候,多数情况是只有满足一定条件的情况下才能打开这把锁:

//主线程中

NSConditionLock *theLock=[[NSConditionLockalloc]init];

//线程1

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{

for(inti=0;i<=2;i++)

{

[theLocklock];

NSLog(@"thread1:%d",i);

sleep(2);

[theLockunlockWithCondition:i];

}

});

//线程2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{

[theLocklockWhenCondition:2];

NSLog(@"thread2");

[theLockunlock];

});

在线程1中的加锁使用了lock,是不需要条件的,所以顺利的就锁住了。但在unlock的使用了一个整型的条件,它可以开启其它线程中正在等待这把钥匙的临界地,而线程2则需要一把被标识为2的钥匙,所以当线程1循环到最后一次的时候,才最终打开了线程2中的阻塞。但即便如此,NSConditionLock也跟其它的锁一样,是需要lock与unlock对应的,只是lock、lockWhenCondition:与unlock,unlockWithCondition:是可以随意组合的,当然这是与你的需求相关的。

6、分布锁:NSDistributedLock

以上所有的锁都是在解决多线程之间的冲突,但如果遇上多个进程或多个程序之间需要构建互斥的情景该怎么办呢?这个时候我们就需要使用到NSDistributedLock了,从它的类名就知道这是一个分布式的Lock,NSDistributedLock的实现是通过文件系统的,所以使用它才可以有效的实现不同进程之间的互斥,但NSDistributedLock并非继承于NSLock,它没有lock方法,它只实现了tryLock,unlock,breakLock,所以如果需要lock的话,你就必须自己实现一个tryLock的轮询。

7、GCD中信号量:dispatch_semaphore

假设现在系统有两个空闲资源可以被利用,但同一时间却有三个线程要进行访问,这种情况下,该如何处理呢?这里,我们就可以方便的利用信号量来解决这个问题。同样我们也可以用它来构建一把”锁”(从本质上讲,信号量与锁是有区别的,具体的请自行查阅资料)。

信号量:就是一种可用来控制访问资源的数量的标识。设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。

在GCD中有三个函数是semaphore的操作:

dispatch_semaphore_create创建一个semaphore

dispatch_semaphore_signal发送一个信号

dispatch_semaphore_wait等待信号

dispatch_semaphore_create函数有一个整形的参数,我们可以理解为信号的总量,dispatch_semaphore_signal是发送一个信号,自然会让信号总量+1,dispatch_semaphore_wait等待信号,当信号总量少于0的时候就会一直等待,否则就可以正常的执行,并让信号总量-1,根据这样的原理,我们便可以快速的创建一个并发控制来同步任务和有限资源访问控制。

8、GCD中“栅栏函数”:dispatch_barrier_async

dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行,该函数需要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用。

dispatch_barrier_async函数的作用:

(1)实现高效率的数据库访问和文件访问

(2)避免数据竞争




自旋锁


    // 自旋锁,性能最高,但已被证明不再安全

   {

        OSSpinLock lock = OS_SPINLOCK_INIT;

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

            OSSpinLockLock(&lock);

            // 待加锁的代码

            OSSpinLockUnlock(&lock);

        }

    }

信号量锁

    // 信号量锁,性能较高

    {

        dispatch_semaphore_t lock =  dispatch_semaphore_create(1);

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

            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);

            // 待加锁的代码

            dispatch_semaphore_signal(lock);

        }

    }

互斥锁


    // 互斥锁,性能较高

    {

        pthread_mutex_t lock;

        pthread_mutex_init(&lock, NULL);

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

            pthread_mutex_lock(&lock);

            // 待加锁的代码

            pthread_mutex_unlock(&lock);

        }

    }

条件锁

    // 条件锁

    {

        NSCondition *lock = [NSCondition new];

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

            [lock lock];

            // 待加锁的代码

            [lock unlock];

        }

    }

普通锁

    // 普通锁

    {

        NSLock *lock = [NSLock new];

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

            [lock lock];

            // 待加锁的代码

            [lock unlock];

        }

    }

递归锁

    // 递归锁

    {

        NSRecursiveLock *lock = [NSRecursiveLock new];

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

            [lock lock];

            // 待加锁的代码

            [lock unlock];

        }

    }

条件锁

    // 条件锁

    {

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

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

            [lock lock];

            // 待加锁的代码

            [lock unlock];

        }

    }

便利锁(自己YY的名)


    // 普通锁,性能最差

    {

        NSObject *lock = [NSObject new];

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

            @synchronized(lock) {

                   // 待加锁的代码

            }

        }

    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 锁是一种同步机制,用于多线程环境中对资源访问的限制iOS中常见锁的性能对比图(摘自:ibireme): iOS锁的...
    LiLS阅读 1,458评论 0 6
  • 在平时的开发中经常使用到多线程,在使用多线程的过程中,难免会遇到资源竞争的问题,那我们怎么来避免出现这种问题那? ...
    IAMCJ阅读 3,033评论 2 25
  • 本文不介绍各种锁的高级用法,只是整理锁相关的知识点,帮助理解。 锁的作用 防止在多线程(多任务)的情况下对共享资源...
    HelloiWorld阅读 2,826评论 0 8
  • 也许是时间太久,生活方式和信念已经不同,来到这个从小长大却久违的地方,很熟悉,也很陌生了。他们谈话的意义依旧没有...
    目月其分阅读 380评论 0 1
  • R:喜欢和怎样的人交往,你就越有可能变成怎样的人。 I:和爱思考的人交往,你能够提升很快,有一部分原因是将这个人当...
    dbnfjfkkf阅读 158评论 0 0