互斥锁(为了实现多个线程对同一共享资源的争用管理)
在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,多人共用一台打印机,为了使大家都能有序公正的使用这台打印机,肯定要进行某种管理。
在线程里有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。
【互斥锁的特点】:
1. 原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;
2. 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;
3. 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。
【互斥锁的操作流程如下】:
1. 在访问共享资源后临界区域前,对互斥锁进行加锁;
2. 在访问完成后释放互斥锁导上的锁。
3. 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。
下面是常用的关于互斥锁的一些函数
#include <pthread.h>
#include <time.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
/* 该函数的功能是初始化一个互斥锁*//*这种初始化互斥锁的方式叫做动态方式*/
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
/*使用静态的方法初始化互斥锁,类似于我们定义一个指针时,将这个指针指向nullptr,
即 char* p =nullptr*/
int pthread_mutex_lock(pthread_mutex_t *mutex);
/* 这个函数的功能是对互斥锁上锁,若此时互斥锁已经上锁,说明此时共享资源正在被使用,则调用者该函数者一直阻塞,直到互斥锁解锁后再上锁。*/
int pthread_mutex_trylock(pthread_mutex_t *mutex);
/* 调用该函数时,若互斥锁未加锁,则上锁,返回 0;若互斥锁已加锁,则函数直接返回失败,即 EBUSY。*/
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict abs_timeout);
/* 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量原语允许绑定线程阻塞时间。*/
int pthread_mutex_unlock(pthread_mutex_t *mutex);
/*对指定的互斥锁解锁。*/
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/* 销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。*/
示例代码【阻塞模式】
//使用互斥量解决多线程抢占资源的问题
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
char* buf[5]; //字符指针数组 全局变量 相当于共享资源
int pos; //用于指定上面数组的下标
//1.定义互斥量
pthread_mutex_t mutex;//相当于互斥锁
void *task(void *p)
{
//3.使用互斥量进行加锁
pthread_mutex_lock(&mutex);
buf[pos] = (char *)p;
sleep(1);
pos++;
//4.使用互斥量进行解锁
pthread_mutex_unlock(&mutex);
}
int main(void)
{
//2.初始化互斥量, 默认属性
pthread_mutex_init(&mutex, NULL);
//1.启动一个线程 向数组中存储内容
pthread_t tid, tid2;
pthread_create(&tid, NULL, task, (void *)"zhangfei");
pthread_create(&tid2, NULL, task, (void *)"guanyu");
//2.主线程进程等待,并且打印最终的结果
pthread_join(tid, NULL);
pthread_join(tid2, NULL);
//5.销毁互斥量
pthread_mutex_destroy(&mutex);
int i = 0;
printf("字符指针数组中的内容是:");
for(i = 0; i < pos; ++i)
{
printf("%s ", buf[i]);
}
printf("\n");
return 0;
}
【多次运行结果】
【示例代码2 】
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
int main (void)
{
int err;
struct timespec tout;//存放时间信息的结构体
struct tm *tmp;
char buf[64];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //静态的定义一个互斥锁
pthread_mutex_lock (&lock);//上锁
printf ("mutex is locked\n");
clock_gettime (CLOCK_REALTIME, &tout);//获取当前时间信息,并存放在tout中
tmp = localtime (&tout.tv_sec); //将保存的时间信息赋值给tmp
strftime (buf, sizeof (buf), "%r", tmp);
printf ("current time is %s\n", buf);//显示当前时间
tout.tv_sec += 10;//将之前已保存的时间加上10秒
err = pthread_mutex_timedlock (&lock, &tout);/*尝试获取互斥锁,若此时互斥锁已上锁,则设定阻塞的时间为10秒*/
clock_gettime (CLOCK_REALTIME, &tout);//获取当前时间信息,并存放在tout中
tmp = localtime (&tout.tv_sec);//将保存的时间信息赋值给tmp
strftime (buf, sizeof (buf), "%r", tmp);
printf ("the time is now %s\n", buf);//显示当前时间
if (err == 0)
printf ("mutex locked again\n");
else
printf ("can`t lock mutex again:%s\n", strerror (err));
pthread_mutex_destroy(&lock);
return 0;
}
【运行结果】