一般每个程序都会有多个线程,也不能确定每个线程所需要的资源都是独立的,如果有两个线程需要同一个资源,且其中一个使用后却将其给释放掉了,那另一个就会得不到资源导致系统卡死,这也便是死锁,这是我们就新加了新的知识:互斥与同步来预防这类问题的发生。
1.概念
一个进程中线程间共享着大量的数据,互斥与同步的机制在程序中便无可避免。
2.互斥锁
(1)特性
互斥锁也叫互斥量,属于pthread线程库中的互斥同步技术,对应的控制操作就是加锁后不能再继续加锁,除非解锁后。
(2)编程使用
//1.声明初始化互斥锁
pthread_mutex_t lock;
pthread_mutex_init(&lock,NULL);
//pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
//2.加锁/获取锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//3.访问共享资源
//4.解锁/释放锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//5.不再使用时销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
例程:
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
char *data[5]={"hello","world","bob","jack","tom"};
//声明互斥锁
pthread_mutex_t lock;
//线程函数1
void *task(void *arg)
{
//加锁
pthread_mutex_lock(&lock);
sleep(1);
printf("task thread id=%lu\n",pthread_self());
//访问共享资源
for(int i=0;i<5;i++){
sleep(1);
printf("task:%d:%s\n",i,data[i]);
};
//解锁
pthread_mutex_unlock(&lock);
pthread_exit(NULL);//退出线程
};
//线程函数2
void *task1(void *arg)
{
//加锁
pthread_mutex_lock(&lock);
sleep(1);
printf("task thread id=%lu\n",pthread_self());
//访问共享资源
for(int i=0;i<5;i++){
sleep(1);
printf("task1:%d:%s\n",i,data[i]);
};
//解锁
pthread_mutex_unlock(&lock);
pthread_exit(NULL);//退出线程
};
int main()
{
//初始化互斥锁
pthread_mutex_init(&lock,NULL);
//1.创建id
pthread_t id1,id2;
//2.创建线程
pthread_create(&id1,NULL,task,NULL);
printf("线程创建成功,id=%lu\n",id1);
pthread_create(&id2,NULL,task1,NULL);
printf("线程创建成功,id=%lu\n",id2);
for(int i=50;i>30;i--){
sleep(1);
}
//销毁锁
pthread_mutex_destroy(&lock);
return 0;
}
3.读写锁
(1)概念
由于互斥锁对读写操作直接进行互斥控制,多个线程经常性同时读写的情况下效率较差,因为对共享资源的读操作无需互斥,可以同时进行,为此添加了读写锁。
读写锁对加锁进行了区分,读操作前加读锁,写操作前加写锁。读锁属于共享锁,写锁属于互斥锁。
(2)编程方法
//1.声明初始化读写锁
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock,NULL);
//2.加锁
//加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrwlock(pthread_rwlock_t *rwlock);
//加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
//3.访问共享资源
//4.解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
//5.销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参考例程:
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
char *data[5]={};
int size=0;
//声明读写锁
pthread_rwlock_t rwlock;
//写线程函数
void *write_task(void *arg)
{
//加写锁
pthread_rwlock_wrlock(&rwlock);
//写共享资源
data[size]=(char *)arg;
sleep(1);
size++;
//解锁
pthread_rwlock_unlock(&rwlock);
pthread_exit(NULL);//退出线程
};
//读线程函数
void *read_task(void *arg)
{
//加读锁
pthread_rwlock_rdlock(&rwlock);
//读共享资源
for(int i=0;i<size;i++){
printf("%lu:%s\n",pthread_self(),data[i]);
sleep(1);
};
//解锁
pthread_rwlock_unlock(&rwlock);
pthread_exit(NULL);//退出线程
};
int main()
{
//初始化读写锁
pthread_rwlock_init(&rwlock,NULL);
//创建id
pthread_t id1,id2,id3,id4,id5;
pthread_create(&id1,NULL,write_task,"BOB");
pthread_create(&id2,NULL,write_task,"TOM");
pthread_create(&id3,NULL,write_task,"JACK");
sleep(2);
pthread_create(&id4,NULL,read_task,NULL);
pthread_create(&id5,NULL,read_task,NULL);
for(int i=0;i<10;i++)
sleep(1);
//销毁锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
4.信号量(POSIX信号量)
(1)概念
线程中的信号量和进程中的信号量原理是相同的,都是一个计数器,用来控制访问共享资源的最大并行线程数。
工作原理:设置初始计数(共享资源个数),每来一个任务访问计数-1(P操作),每退出一个任务计数+1(V操作),当计数值为0时不允许任务访问,直到计数值重新大于0为止。
POSIX信号量不属于线程库的范畴,使用时需要包含头文件——semaphore.h
如果初始计数值为1,效果等同于互斥锁。
POSIX信号量分为无名信号量和有名信号量。