1.基本概念
(1)互斥锁的概念
互斥锁:对共享数据进行锁定,保证同一时刻只能有一个线程去操作。
抢到锁的线程先执行,没有抢到锁的线程需要等待,等锁用完后需要释放,然后其它等待的线程再去抢这个锁,那个线程抢到那个线程再执行。 具体那个线程抢到这个锁我们决定不了,是由cpu调度决定的,但是可以通过延时来控制谁先进行。
(2)互斥锁为资源引入一个状态:锁定/非锁定
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
(3)使用互斥锁的目的
能够保证多个线程访问共享数据不会出现资源竞争及数据错误
互斥锁是通过锁的机制来实现线程间的同步问题。互斥锁的基本流程为:
- 初始化一个互斥锁:
pthread_mutex_init()
函数 - 加锁:
pthread_mutex_lock()
函数或者pthread_mutex_trylock()
函数 - 对共享资源的操作
- 解锁:
pthread_mutex_unlock()
函数 - 注销互斥锁:
pthread_mutex_destory()
函数
其中,在加锁过程中,pthread_mutex_lock()
函数和pthread_mutex_trylock()
函数的过程略有不同:
- 当使用
pthread_mutex_lock()
函数进行加锁时,若此时已经被锁,则尝试加锁的线程会被阻塞,直到互斥锁被其他线程释放,当pthread_mutex_lock()
函数有返回值时,说明加锁成功; - 而使用
pthread_mutex_trylock()
函数进行加锁时,若此时已经被锁,则会返回EBUSY
的错误码。
同时,解锁的过程中,也需要满足两个条件:
- 解锁前,互斥锁必须处于锁定状态;
- 必须由加锁的线程进行解锁。
当互斥锁使用完成后,必须进行清除。
2.例子
pthread_mutex.c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
// 两个线程函数声明
void *thread1_function(void *arg);
void *thread2_function(void *arg);
int count = 0;
pthread_mutex_t mutex; // 互斥锁
int main(void)
{
pthread_t pthread1, pthread2;
int ret;
pthread_mutex_init(&mutex, NULL); // 互斥锁初始化,第二个参数可以空指针
ret = pthread_create(&pthread1, NULL, thread1_function, NULL);
if(ret != 0)
{
perror("pthread_create");
exit(1);
}
ret = pthread_create(&pthread2, NULL, thread2_function, NULL);
if(ret != 0)
{
perror("pthread_craet");
exit(1);
}
pthread_join(pthread1, NULL); //监测线程1结束
pthread_join(pthread2, NULL); //监测线程2结束
printf("the thred is over ,process is over too.\n");
return 0;
}
//线程1 线程所有资源依赖于进程,进程结束线程也会结束
void *thread1_function(void *arg) // arg参数(指针类型)
{
int i = 1;
while(1)
{
pthread_mutex_lock(&mutex); // 互斥锁上锁
for(i = 0; i < 10; i++) // 上锁状态
{
printf("hello world\n");
sleep(1); // 1秒间隔
}
pthread_mutex_unlock(&mutex); // 互斥锁解锁
sleep(1);
}
return NULL;
}
//线程2
void *thread2_function(void *arg)
{
int i;
sleep(1); // 一秒后上锁
while(1)
{
pthread_mutex_lock(&mutex); // 互斥锁上锁
for(i = 0; i < 10; i++) // 上锁状态
{
printf("good morning\n");
sleep(1); // 1秒间隔
}
pthread_mutex_unlock(&mutex); // 互斥锁解锁
sleep(1);
}
return NULL;
}
编译:gcc pthread_mutex.c -pthrea
结果: 一个线程运行时另一个无法运行,一直循环
加锁后的火车售票系统 ---避免出现负数票
#include <stdio.h>
#include <pthread.h>
int cnt = 121;
pthread_mutex_t lock; // 互斥锁
void* pthread1(void* args) // 线程1
{
while(1)
{
pthread_mutex_lock(&lock); //因为要访问全局的共享变量,所以就要加锁
if(cnt > 0) //如果有票
{
printf("窗口A开始售票,车票是:%d\n",cnt);
sleep(2);
cnt--;
printf("窗口A售票结束,最后一张车票是:%d\n",cnt);
}
else
{
pthread_mutex_unlock(&lock);
pthread_exit(NULL);
}
pthread_mutex_unlock(&lock);
sleep(1); //要放到锁的外面,让另一个有时间锁
}
}
void* pthread2(void* args) // 线程2
{
while(1)
{
pthread_mutex_lock(&lock); // 上锁
if(cnt>0)
{
printf("窗口B开始售票--车票是:%d\n",cnt);
sleep(2);
cnt--;
printf("窗口B售票结束,最后一张车票是:%d\n",cnt);
}
else
{
pthread_mutex_unlock(&lock); // 解锁
pthread_exit(NULL);
}
pthread_mutex_unlock(&lock); // 解锁
sleep(1);
}
}
int main()
{
pthread_t pthid1 = 0;
pthread_t pthid2 = 0;
//初始化锁
pthread_mutex_init(&lock,NULL);
//创建线程
pthread_create(&pthid1,NULL,pthread1,NULL);
pthread_create(&pthid2,NULL,pthread2,NULL);
//等待线程结束
pthread_join(pthid1,NULL);
pthread_join(pthid2,NULL);
//销毁锁
pthread_mutex_destroy(&lock);
return 0;
}