互斥量(Mutex)
互斥量是实现最简单的锁类型,因此有一些教科书一般以互斥量为例对锁原语进行描述。互斥量的释放并不仅仅依赖于释放 *** 作,还可以引入一个定时器属性。如果在释放 *** 作执行前发生定时器超时,则互斥量也会释放代码块或共享存储区供其他线程访问。当有异常发生时,可使用try-finally语句来确保互斥量被释放。定时器状态或try-finally语句的使用可以避免产生死锁。
递归锁(Recursive
Lock)
递归锁是指可以被当前持有该锁的线程重复获取,而不会导致该线程产生死锁的锁类型。对递归锁而言,只有在当前持有线程的获取锁 *** 作都有一个释放 *** 作与之对应时,其他线程才可以获取该锁。因此,在使用递归锁时,必须要用足够的释放锁 *** 作来平衡获取锁 *** 作,实现这一目标的最佳方式是在单入口单出口代码块的两头一一对应地使用获取、释放 *** 作,做法和在普通锁中一样。递归锁在递归函数中最有用。但是,总的来说,递归锁比非递归锁速度要慢。需要注意的是:调用线程获得几次递归锁必须释放几次递归锁。
以下为一个递归锁的示例:
[cpp] view plain copy
Recursive_Lock L
void recursiveFunction (int count) {
L->acquire()
if (count >0) {
count = count - 1
recursiveFunction(count)
}
L->release()
}
读写锁(Read-Write
lock) 读写锁又称为共享独占锁(shared-exclusive
lock)、多读单写锁(multiple-read/single-write lock)或者非互斥信号量(non-mutual
exclusion
semaphore)。读写锁允许多个线程同时进行读访问,但是在某一时刻却最多只能由一个线程执行写 *** 作。对于多个线程需要同时读共享数据却并不一定进行写 *** 作的应用来说,读写锁是一种高效的同步机制。对于较长的共享数据,只为其设置一个读写锁会导致较长的访问时间,最好将其划分为多个小段并设置多个读写锁以进行同步。
这个读写锁我们在学习数据库的时候应该很熟悉的哟!
旋转锁(Spin
Lock)
旋转锁是一种非阻塞锁,由某个线程独占。采用旋转锁时,等待线程并不静态地阻塞在同步点,而是必须“旋转”,不断尝试直到最终获得该锁。旋转锁多用于多处理器系统中。这是因为,如果在单核处理器中采用旋转锁,当一个线程正在“旋转”时,将没有执行资源可供另一释放锁的线程使用。旋转锁适合于任何锁持有时间少于将一个线程阻塞和唤醒所需时间的场合。线程控制的变更,包括线程上下文的切换和线程数据结构的更新,可能比旋转锁需要更多的指令周期。旋转锁的持有时间应该限制在线程上下文切换时间的50%到100%之间(Kleiman,1996年)。在线程调用其他子系统时,线程不应持有旋转锁。对旋转锁的不当使用可能会导致线程饿死,因此需谨慎使用这种锁机制。旋转锁导致的饿死问题可使用排队技术来解决,即每个等待线程按照先进先出的顺序或者队列结构在一个独立的局部标识上进行旋转。
学习了这些,果然受益匪浅,在今后的coding中,我得挨个试试咯。
From : https://blog.csdn.net/qq_39382769/article/details/960753461.同一个线程内部,指令按照先后顺序执行;但不同线程之间的指令很难说清楚是哪一个先执行,在并发情况下,指令执行的先后顺序由内核决定。 如果运行的结果依赖于不同线程执行的先后的话,那么就会形成竞争条件,在这样的情况下,计算的结果很难预知,所以应该尽量避免竞争条件的形成。 2.最常见的解决竞争条件的方法是:将原先分离的两个指令构成一个不可分割的原子 *** 作,而其他任务不能插入到原子 *** 作中! 3.对多线程来说,同步指的是在一定时间内只允许某一个线程访问某个资源,而在此时间内,不允许其他线程访问该资源! 互斥锁 条件变量 读写锁 信号量 一种特殊的全局变量,拥有lock和unlock两种状态。 unlock的互斥锁可以由某个线程获得,一旦获得,这个互斥锁会锁上变成lock状态,此后只有该线程由权力打开该锁,其他线程想要获得互斥锁,必须得到互斥锁再次被打开之后。 1.互斥锁的初始化, 分为静态初始化和动态初始化. 2.互斥锁的相关属性及分类 (1) attr表示互斥锁的属性 (2) pshared表示互斥锁的共享属性,由两种取值: 1)PTHREAD_PROCESS_PRIVATE:锁只能用于一个进程内部的两个线程进行互斥(默认情况) 2)PTHREAD_PROCESS_SHARED:锁可用于两个不同进程中的线程进行互斥,使用时还需要在进程共享内存中分配互斥锁,然后为该互斥锁指定属性就可以了。 互斥锁存在缺点: (1)某个线程正在等待共享数据内某个条件出现。 (2)重复对数据对象加锁和解锁(轮询),但是这样轮询非常耗费时间和资源,而且效率非常低,所以互斥锁不太适合这种情况。 当线程在等待满足某些条件时,使线程进入睡眠状态;一旦条件满足,就换线因等待满足特定条件而睡眠的线程。 程序的效率无疑会大大提高。 1)创建 静态方式:pthread_cond_t cond PTHREAD_COND_INITIALIZER 动态方式:int pthread_cond_init(&cond,NULL) Linux thread 实现的条件变量不支持属性,所以NULL(cond_attr参数) 2)注销 int pthread_cond_destory(&cond) 只有没有线程在该条件变量上,该条件变量才能注销,否则返回EBUSY 因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程!(请参考条件变量的底层实现) 3)等待 条件等待:int pthread_cond_wait(&cond,&mutex) 计时等待:int pthread_cond_timewait(&cond,&mutex,time) 1.其中计时等待如果在给定时刻前条件没有被满足,则返回ETIMEOUT,结束等待 2.无论那种等待方式,都必须有一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait形成竞争条件! 3.在调用pthread_cond_wait前必须由本线程加锁 4)激发 激发一个等待线程:pthread_cond_signal(&cond) 激发所有等待线程:pthread_cond_broadcast(&cond) 重要的是,pthread_cond_signal不会存在惊群效应,也就是是它最多给一个等待线程发信号,不会给所有线程发信号唤醒,然后要求他们自己去争抢资源! pthread_cond_broadcast() 唤醒所有正在pthread_cond_wait()的同一个条件变量的线程。注意:如果等待的多个现场不使用同一个锁,被唤醒的多个线程执行是并发的。pthread_cond_broadcast &pthread_cond_signal1.读写锁比互斥锁更加具有适用性和并行性 2.读写锁最适用于对数据结构的读 *** 作读 *** 作次数多余写 *** 作次数的场合! 3.锁处于读模式时可以线程共享,而锁处于写模式时只能独占,所以读写锁又叫做共享-独占锁。 4.读写锁有两种策略:强读同步和强写同步 强读同步: 总是给读者更高的优先权,只要写者没有进行写 *** 作,读者就可以获得访问权限 强写同步: 总是给写者更高的优先权,读者只能等到所有正在等待或者执行的写者完成后才能进行读 1)初始化的销毁读写锁 静态初始化:pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER 动态初始化:int pthread_rwlock_init(rwlock,NULL),NULL代表读写锁采用默认属性 销毁读写锁:int pthread_rwlock_destory(rwlock) 在释放某个读写锁的资源之前,需要先通过pthread_rwlock_destory函数对读写锁进行清理。释放由pthread_rwlock_init函数分配的资源 如果你想要读写锁使用非默认属性,则attr不能为NULL,得给attr赋值 int pthread_rwlockattr_init(attr),给attr初始化 int pthread_rwlockattr_destory(attr),销毁attr 2)以写的方式获取锁,以读的方式获取锁,释放读写锁 int pthread_rwlock_rdlock(rwlock),以读的方式获取锁 int pthread_rwlock_wrlock(rwlock),以写的方式获取锁 int pthread_rwlock_unlock(rwlock),释放锁 上面两个获取锁的方式都是阻塞的函数,也就是说获取不到锁的话,调用线程不是立即返回,而是阻塞执行,在需要进行写 *** 作的时候,这种阻塞式获取锁的方式是非常不好的,你想一下,我需要进行写 *** 作,不但没有获取到锁,我还一直在这里等待,大大拖累效率 所以我们应该采用非阻塞的方式获取锁: int pthread_rwlock_tryrdlock(rwlock) int pthread_rwlock_trywrlock(rwlock) 互斥锁只允许一个线程进入临界区,而信号量允许多个线程进入临界区。 1)信号量初始化 int sem_init(&sem,pshared, v) pshared为0,表示这个信号量是当前进程的局部信号量。 pshared为1,表示这个信号量可以在多个进程之间共享。 v为信号量的初始值。 返回值: 成功:0,失败:-1 2)信号量值的加减 int sem_wait(&sem):以原子 *** 作的方式将信号量的值减去1 int sem_post(&sem):以原子 *** 作的方式将信号量的值加上1 3)对信号量进行清理 int sem_destory(&sem)编译时要用到pthread 库:gcc -lpthread
错误码位置:/usr/include/asm-generic/errno.h
gcc pthread_create.c -lpthread
思考:主子线程交替打印奇数偶数。
思考:证明线程可以自己取消自己。
思考:证明SIGKILL和SIGSTOP 是无法阻塞的。
/usr/include/bits/pthreadtypes.h中查看pthread_mutex_t
思考:用多线程将一个文件1.c拷贝3个副本,11.c,12.c,13.c
思考:多个生产者和消费者
思考:将互斥量等初始化使用pthread_once实现。
思考:设置线程的分离属性,然后在新县城中获取自己的分离属性。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)