线程通信 – 互斥
临界资源
一次只允许一个任务(进程、线程)访问的共享资源
临界区 访问临界资源的代码
互斥机制 mutex互斥锁 任务访问临界资源前申请锁,访问完后释放锁
互斥锁初始化 – pthread_mutex_init
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t * attr);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
attr 互斥锁属性,
NULL表示缺省属性
man 函数出现 No manual entry for pthread_mutex_xxx解决办法 apt-get install manpages-posix-dev
互斥锁销毁 pthread_mutex_destroy
int pthread_mutex_destroy(pthread_mutex_t *mutex)
申请锁 – pthread_mutex_lock
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex)
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
pthread_mutex_lock 如果无法获得锁,任务阻塞
pthread_mutex_trylock 如果无法获得锁,返回EBUSY而不是挂起等待
释放锁 – pthread_mutex_unlock
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex 指向要初始化的互斥锁对象
执行完临界区要及时释放锁
必要性: 临界资源不可以共享
man手册找不到 pthread_mutex_xxxxxxx (提示No manual entry for pthread_mutex_xxx)的解决方法:
apt-get install manpages-posix-dev
互斥锁的创建和销毁
两种方法创建互斥锁,静态方式和动态方式
动态方式:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
其中mutexattr用于指定互斥锁属性,如果为NULL则使用缺省属性。
静态方式:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
锁的销毁:
int pthread_mutex_destroy(pthread_mutex_t *mutex)
在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。
互斥锁的使用:
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
vim 设置代码全文格式化:gg=G
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
FILE *fp;
void *func2(void *arg){
pthread_detach(pthread_self());
printf("This func2 thread\n");
char str[]="I write func2 line\n";
char c;
int i=0;
while(1){
pthread_mutex_lock(&mutex);
while(i<strlen(str))
{
c = str[i];
fputc(c,fp);
usleep(1);
i++;
}
pthread_mutex_unlock(&mutex);
i=0;
usleep(1);
}
pthread_exit("func2 exit");
}
void *func(void *arg){
pthread_detach(pthread_self());
printf("This is func1 thread\n");
char str[]="You read func1 thread\n";
char c;
int i=0;
while(1){
pthread_mutex_lock(&mutex);
while(i<strlen(str))
{
c = str[i];
fputc(c,fp);
i++;
usleep(1);
}
pthread_mutex_unlock(&mutex);
i=0;
usleep(1);
}
pthread_exit("func1 exit");
}
int main(){
pthread_t tid,tid2;
void *retv;
int i;
fp = fopen("1.txt","a+");
if(fp==NULL){
perror("fopen");
return 0;
}
pthread_create(&tid,NULL,func,NULL);
pthread_create(&tid2,NULL,func2,NULL);
while(1){
sleep(1);
}
}
读写锁(Read-Write Lock)和互斥锁(Mutex Lock)是两种常见的线程同步机制,它们适用于不同的应用场景。
互斥锁适用于对共享资源进行临界区保护的情况,即在同一时刻只允许一个线程访问共享资源。互斥锁的特点是:当某个线程获取到互斥锁后,其他线程必须等待该线程释放锁才能继续访问共享资源。适用场景包括:访问共享变量、访问共享数据结构、保护关键代码段等。
读写锁适用于读操作远远多于写操作的情况。读写锁允许多个线程同时对共享资源进行读取,但只允许一个线程进行写入操作。读写锁的特点是:当没有线程对共享资源进行写入时,多个线程可以同时读取该资源;当有线程对共享资源进行写入时,其他线程无法进行读取和写入操作。适用场景包括:数据库读取、缓存操作、读多写少的数据结构等。
总结来说,互斥锁适用于对共享资源的独占访问,而读写锁适用于读多写少的场景,可以提高并发读取的效率。选择哪种锁取决于线程对共享资源的读写比例和并发性需求。
读写锁
必要性:提高线程执行效率
特性:
写者:写者使用写锁,如果当前没有读者,也没有其他写者,写者立即获得写锁;否则写者将等待,直到没有读者和写者。
读者:读者使用读锁,如果当前没有写者,读者立即获得读锁;否则读者等待,直到没有写者。
注意:
同一时刻只有一个线程可以获得写锁,同一时刻可以有多个线程获得读锁。
读写锁出于写锁状态时,所有试图对读写锁加锁的线程,不管是读者试图加读锁,还是写者试图加写锁,都会被阻塞。
读写锁处于读锁状态时,有写者试图加写锁时,之后的其他线程的读锁请求会被阻塞,以避免写者长时间的不写锁
初始化一个读写锁 pthread_rwlock_init
读锁定读写锁 pthread_rwlock_rdlock
非阻塞读锁定 pthread_rwlock_tryrdlock
写锁定读写锁 pthread_rwlock_wrlock
非阻塞写锁定 pthread_rwlock_trywrlock
解锁读写锁 pthread_rwlock_unlock
释放读写锁 pthread_rwlock_destroy
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
pthread_rwlock_t rwlock;
FILE *fp;
void * read_func(void *arg){
pthread_detach(pthread_self());
printf("read thread\n");
char buf[32]={0};
while(1){
//rewind(fp);
pthread_rwlock_rdlock(&rwlock);
while(fgets(buf,32,fp)!=NULL){
printf("%d,rd=%s\n",(int)arg,buf);
usleep(1000);
}
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
void *func2(void *arg){
pthread_detach(pthread_self());
printf("This func2 thread\n");
char str[]="I write func2 line\n";
char c;
int i=0;
while(1){
pthread_rwlock_wrlock(&rwlock);
while(i<strlen(str))
{
c = str[i];
fputc(c,fp);
usleep(1);
i++;
}
pthread_rwlock_unlock(&rwlock);
i=0;
usleep(1);
}
pthread_exit("func2 exit");
}
void *func(void *arg){
pthread_detach(pthread_self());
printf("This is func1 thread\n");
char str[]="You read func1 thread\n";
char c;
int i=0;
while(1){
pthread_rwlock_wrlock(&rwlock);
while(i<strlen(str))
{
c = str[i];
fputc(c,fp);
i++;
usleep(1);
}
pthread_rwlock_unlock(&rwlock);
i=0;
usleep(1);
}
pthread_exit("func1 exit");
}
int main(){
pthread_t tid1,tid2,tid3,tid4;
void *retv;
int i;
fp = fopen("1.txt","a+");
if(fp==NULL){
perror("fopen");
return 0;
}
pthread_rwlock_init(&rwlock,NULL);
pthread_create(&tid1,NULL,read_func,1);
pthread_create(&tid2,NULL,read_func,2);
pthread_create(&tid3,NULL,func,NULL);
pthread_create(&tid4,NULL,func2,NULL);
while(1){
sleep(1);
}
}
死锁
概念:
死锁是指在多进程或多线程系统中,两个或多个进程(线程)因为竞争共享资源而陷入无限等待的状态,无法继续执行下去,称为死锁。
在死锁状态下,每个进程都在等待其他进程释放它所需要的资源,而且没有任何一方能够进行下去。这种情况下,系统无法继续运行,需要通过外部的干预来解除死锁。
死锁产生的原因通常是以下四个条件同时满足:
1. 互斥条件(Mutual Exclusion):某些资源一次只能被一个进程(线程)所占用,也就是说这些资源不能被共享。
2. 请求与保持条件(Hold and Wait):一个进程(线程)在请求一些资源的同时,继续保持着已经获得的资源。
3. 不可剥夺条件(No Preemption):已经分配给某个进程(线程)的资源不能被强制剥夺,只能在进程(线程)用完之后自愿释放。
4. 环路等待条件(Circular Wait):存在一个进程(线程)的等待序列,使得每个进程(线程)都在等待下一个进程(线程)所持有的资源。
当这四个条件同时满足,并且多个进程(线程)之间形成了一个环路等待的关系,就会导致死锁的产生。
死锁是一个严重的问题,会导致资源的浪费和系统的停滞。为了避免死锁的发生,可以采取一些策略,如资源分配策略、避免策略、检测与恢复策略等。
避免方法:
- 锁越少越好,最好使用一把锁
- 调整好锁的顺序
- 解决办法:(1)将线程用到的线程锁的顺序一致,可以避免死锁;(2)增加线程休眠时间,也可以避免死锁
1. 预防死锁:通过合理地分配资源、避免资源竞争、避免进程持有多个资源以及进行资源的不可剥夺性等方式来预防死锁的发生。
2. 避免循环等待:引入资源有序性的概念,要求所有进程按照相同的顺序请求资源,以破坏循环等待条件。
3. 资源剥夺:当一个进程请求新的资源时,如果无法分配,则释放该进程已经持有的资源,以避免循环等待。
4. 资源预约:一个进程在申请资源时,请求所有需要的资源,并且只有当所有资源都可用时才执行,以避免等待其他资源导致的死锁。
5. 死锁检测与恢复:通过周期性地检测系统状态,判断是否存在死锁,并采取相应的措施来解除或恢复死锁。
6. 强制执行资源分配顺序:对资源进行编号,进程只能按照编号递增的顺序来申请资源,以避免循环等待。
7. 动态资源分配:根据系统状态和资源请求情况,动态地分配资源,以尽量避免死锁的发生。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
FILE *fp;
void *func2(void *arg){
pthread_detach(pthread_self());
printf("This func2 thread\n");
char str[]="I write func2 line\n";
char c;
int i=0;
while(1){
pthread_mutex_lock(&mutex);
printf("%d,I got lock2\n",(int)arg);
sleep(1);
pthread_mutex_lock(&mutex2);
printf("%d,I got 2 locks\n",(int)arg);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
sleep(10);
}
pthread_exit("func2 exit");
}
void *func(void *arg){
pthread_detach(pthread_self());
printf("This is func1 thread\n");
char str[]="You read func1 thread\n";
char c;
int i=0;
while(1){
pthread_mutex_lock(&mutex);
printf("%d,I got lock1\n",(int)arg);
sleep(1);
pthread_mutex_lock(&mutex2);
printf("%d,I got 2 locks\n",(int)arg);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex);
sleep(10);
}
pthread_exit("func1 exit");
}
int main(){
pthread_t tid,tid2;
void *retv;
int i;
fp = fopen("1.txt","a+");
if(fp==NULL){
perror("fopen");
return 0;
}
pthread_create(&tid,NULL,func,1);
pthread_create(&tid2,NULL,func2,2);
while(1){
sleep(1);
}
}