有关互斥量

创建和销毁互斥量

pthread_mutex_t_mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

大部分时间你可能在"文件范围"内,即函数体外,声明互斥量为外部或静态存储类型。如果有其他文件使用互斥量,即将其声明为外部类型;如果仅在本文件中使用,则将其声明为静态类型。应该使用宏PTHREAD_MUTEX_INITIALIZER来声明具有默认属性的静态互斥量。

通常不能静态地初始化一个互斥量,如使用malloc动态分配一个包含互斥量的数据结构时。这时,应该使用pthread_mutex_init调用来动态地初始化互斥量。也可以动态地初始化静态声明的互斥量,但必须保证每个互斥量在使用前被初始化,而且只被初始化一次。可以在创建任何线程之前初始化它,如通过调用pthread_once。若需要初始化一个非缺省属性的互斥量,必须使用动态初始化。
当不再需要一个通过pthread_mutex_init调用动态初始化的互斥量时,应该调用pthread_mutex_destroy来释放它。不需要释放一个使用PTHREAD_MUTEX_INITIALIZER宏静态初始化的互斥量。

加锁和解锁互斥量
int pthread_mutex_lock (pthread_mutex_t *mutex);
int pthread_mutex_trylock (pthread_mutex_t *mutex);
int pthread_mutex_unlock (pthread_mutex_t *mutex);

当调用线程已经锁住互斥量之后,就不能再加锁该互斥量。试图这样做的结果可能是返回错误(EDEADLK), 或者可能陷入"自死锁",使不幸的线程永远等待下去。不能解锁一个已经解锁的互斥量,也不能解锁由其他线程锁住的互斥量。锁住的互斥量是属于加锁线程的。

sched_yield效果:将处理器交给另一个等待运行的线程,但是如果没有就绪的线程,就立即返回。

当需要保护两个共享变量时,有两种基本策略:可以为每个变量指派一个“小”互斥量,或为两个变量指派一个“大”的互斥量。哪一种方法更好取决于很多因素。以下是主要的设计因素:
1 互斥量不是免费的,需要时间加锁和解锁。互斥量应该尽量少,够用即可,每个互斥量保护的区域则尽量大。
2 互斥量的本质是串行执行。若很多线程需要频繁地加锁在同一个互斥量,则线程的大部分时间就会在等待,这对性能是在害的。如果互斥量保护的数据包含彼此无关的片段,则可以将大的互斥量分解为几个小的互斥量来提高性能。这样,任意时刻需要小互斥量的线程减少,线程等待的时间就会减少。所以,互斥量应该足够多(到有意义的地步),每个互斥量保护的区域则应尽量的少。

使用多个互斥量:同时使用多个互斥量会导致复杂度的增加。最坏的情况就是死锁的发生,即两个线程分别锁住一个互斥量而等待对方的互斥量。更多的是像优先级倒置这样的微妙问题(当你将互斥量和优先级调用组合使用时)。

加锁层次:若可以在独立的数据上使用两个分离的互斥量,那么就应该这样做。这样,通过减少线程必须等待其他线程完成数据操作(甚至是该线程不需要的数据)的时间。若数据独立,则某个特定函数就不太可能经常需要同时加锁两个互斥量。
当数据不是完全独立的时候,情况就复杂了。如果你的程序有一个不变量,影响着由两个互斥量保护的数据,即使该不变量被改变或引用,你迟早需要编写同时锁住两个互斥量的代码,来确保不变量的完整。

两个锁的死锁:
有互斥量A B 若一个线程锁住互斥量A后,加锁互斥量B;同时,另一个线程锁住B而等待A,则代码就产生了一个经典的死锁现象。

针对死锁有解决方法:
固定加锁层次:所有需要同时加锁互斥量A和互斥量B的代码,必须首先加锁互斥量A,然后加锁互斥量B,即固定加锁顺序。
试加锁和回退:
在锁住某个集合中的第一个互斥量后,使用pthread_mutex_trylock来加锁集合中的其他互斥量,如果失败则将集合中所有已加锁互斥量释放,并重新加锁。

链锁:是层次锁的一个特殊实例,即两个锁的作用范围互相交叠。当锁住第一个互斥量后,代码进入一个区域,该区域需要另一个互斥量。当锁住另一个互斥量后,第一个互斥量就不再需要,可以释放它了。这种技巧在遍历如树型结构或链表结构时十分有用。每一个节点设置一个互斥量,而不是用一个互斥量锁住整个数据结构,阻止任何并行访问。如:遍历代码可以首先锁住队列头或树根节点,找到期望的节点,锁住它,然后释放根节点或队列头互斥量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 C 语言程序,它使用了共享内存和互斥来实现进程间的通信和同步: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #define SHM_SIZE 1024 struct shared_memory { int counter; }; int main() { int shmid; struct shared_memory *shm; pthread_mutex_t *mutex; // 创建共享内存 shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666); if (shmid < 0) { perror("shmget"); exit(1); } // 将共享内存附加到当前进程 shm = (struct shared_memory *) shmat(shmid, NULL, 0); if (shm == (struct shared_memory *) -1) { perror("shmat"); exit(1); } // 初始化共享内存 shm->counter = 0; // 创建互斥 mutex = (pthread_mutex_t *) shmat(shmid, NULL, 0); if (mutex == (pthread_mutex_t *) -1) { perror("shmat"); exit(1); } pthread_mutex_init(mutex, NULL); // 创建子进程 pid_t pid = fork(); if (pid < 0) { perror("fork"); exit(1); } else if (pid == 0) { // 子进程 int i; for (i = 0; i < 10; i++) { // 加锁 pthread_mutex_lock(mutex); // 计数器加一 shm->counter++; // 解锁 pthread_mutex_unlock(mutex); sleep(1); } } else { // 父进程 int i; for (i = 0; i < 10; i++) { // 加锁 pthread_mutex_lock(mutex); // 计数器加一 shm->counter++; // 解锁 pthread_mutex_unlock(mutex); sleep(1); } } // 销毁互斥 pthread_mutex_destroy(mutex); // 将共享内存从当前进程中分离 shmdt(shm); shmdt(mutex); // 删除共享内存 shmctl(shmid, IPC_RMID, NULL); return 0; } ``` 这个程序使用了 `shmget` 函数来创建共享内存,`shmat` 函数将共享内存附加到当前进程,`pthread_mutex_init` 函数创建互斥。子进程和父进程都会对共享内存中的计数器进行 10 次加一操作,每次加一之前需要先使用互斥进行加锁,加一完成后再解锁。最后,使用 `pthread_mutex_destroy` 函数销毁互斥,并使用 `shmdt` 函数将共享内存从当前进程中分离,最终使用 `shmctl` 函数删除共享内存。 注意:在使用共享内存和互斥时,需要注意避免死锁和竞态条件等问题。建议在使用时仔细阅读相关文档和示例程序,以确保程序的正确性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值