0.线程互斥的重要知识
(1).临界资源
一次只能允许一个任务(进程、线程)访问的共享资源,例如全局变量等。
(2).临界区
访问临界资源的代码
(3).互斥机制
①.互斥锁 - mutex: 任务访问临界资源前申请锁,访问后释放锁。 没申请到锁的 任务就进入阻塞,等待申请到锁后才能操作临界资源
一.互斥锁 - mutex
互斥锁:任务访问临界资源前申请锁,访问后释放锁。 没申请到锁的任务就进入阻塞,等待申请到锁后才能操作临界资源。
1.互斥锁初始化 - pthread_mutex_init
功能:
初始化锁
成功时返回(0),失败时返回(错误码)
参数:
mutex:指向要初始化的互斥锁对象
attr :锁的属性,默认传入NULL
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
2.申请锁 - pthread_mutex_lock
功能:
执行临界区前申请锁,如果当前的锁是空闲的(锁没有被其他任务占有),则能申请到锁,程序继续往下执行。若无法获得锁(其他任务持有该锁),则任务阻塞等待其他任务释放锁。
成功返回(0),失败时返回(错误码);
参数:
mutex:要申请的互斥锁对象
int pthread_mutex_lock(pthread_mutex_t *mutex);
3.释放锁 - pthread_mutex_unlock
功能:
执行完临界区后释放锁,若没有及时释放锁,会导致其他申请锁的任务一直被阻塞;
成功时返回(0),失败时返回(错误码)
参数:
mutex:要释放的互斥锁对象
int pthread_mutex_unlock(pthread_mutex_t *mutex);
二.线程互斥代码实现(利用互斥锁)
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#define __LOCK_
void *thread_func(void *arg);
/* 定义的临界资源 */
unsigned int count,value1,value2;
pthread_mutex_t lock; //定义锁
int main(void)
{
/* 定义线程对象 */
pthread_t a_thread;
/* 1.初始化锁 */
if(0 != pthread_mutex_init(&lock,NULL))
{
printf("fail to pthread_mutex_init\n");
exit(-1);
}
/* 2.创建线程 */
if(0 != pthread_create(&a_thread,NULL,thread_func,NULL))
{
printf("fail to pthread_create\n");
exit(-1);
}
while(1)
{
count++;
#ifdef __LOCK_
/* 3.申请锁,阻塞等待其他任务释放锁 */
pthread_mutex_lock(&lock);
#endif
/* 4.申请到锁后,操作临界资源 */
value1 = count;
value2 = count;
#ifdef __LOCK_
/* 5.操作完临界资源后释放锁,使其他任务可申请该锁 */
pthread_mutex_unlock(&lock);
#endif
}
return 0;
}
void *thread_func(void *arg)
{
while(1)
{
#ifdef __LOCK_
/* 1.申请锁,阻塞等待其他任务释放锁 */
pthread_mutex_lock(&lock);
#endif
/* 2.申请到锁之后,操作临界资源 */
if(value1 != value2)
{
printf("value1 = %u,value2 = %u\n",value1,value2);
usleep(100000);
}
#ifdef __LOCK_
/* 3.操作完临界资源后,释放锁,使其他任务可申请该锁 */
pthread_mutex_unlock(&lock);
#endif
}
return NULL;
}
总结:
可见,加入互斥锁后,不会存在value1 != value2 的情况,即执行下面这段代码的时候不会被打断。即加锁后,其他任务无法对该临界资源进行操作
/* 4.申请到锁后,操作临界资源 */
value1 = count;
value2 = count;