因为一个进程中的各线程对共享资源都具有访问权限,就会产生竞争关系,所以需要互斥锁。
1、互斥锁的过程:
线程X加锁
线程X访问共享资源
线程A解锁
注意:互斥锁是建议锁,不具有强制性(即:不能阻止其他无锁的线程访问另一线程锁内的共享资源)
解释:如果线程A加锁,
线程B加锁,
线程C未加锁,
线程B需要等到线程A解锁,才能访问共享资源,但是线程C可以不等线程A解锁就直接成功访问共享资源,这样就造成了访问资源混乱。
因此:多个线程都要访问共享资源,在访问共享资源时,都要先加锁
2、互斥量
pthread_mutex_t mutex;
mutex只有0和1两个值。mutex初值为1,加锁成功mutex减1由1变为0,解锁成功mutext加1,由0变为1;
3、互斥量函数
1)pthread_mutex_init//对互斥量进行初始化
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
mutex==1
也可以使用:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER,这样就不需要pthread_mutex_destroy了。
2)pthread_mutex_destroy//销毁互斥量
pthread_mutex_detroy(&mutex);
与pthread_mutex_init()进行对应,需要对互斥量进行销毁。
3)pthread_mutex_lock//对互斥量进行加锁
mutex--
pthread_mutex_lock(&mutex);
4)pthread_mutex_unlock//对互斥量进行解锁
mutex++
pthread_mutex_unlock(&mutex);
5)pthread_mutex_trylock//尝试对互斥量进行加锁
如果未加锁,就加锁,如果已加锁,就返回一个异常值。
与pthread_mutex_lock的区别:pthread_mutex_lock时,如果锁未被释放,则线程阻塞等待;
pthread_mutex_trylock则如果锁未被释放,线程不阻塞等待,直接返回,需要轮询。
int ret = pthread_mutex_trylock(&mutex);
if(ret==0)
{
//do sth.
pthread_mutex_unlock(&mutex);
}else{
//do sth.
}
4、应用实例
pthread_mutex_t mutex;
void* func(void *arg)
{
srand(time(NULL));
while(1)
{
pthread_mutex_lock(&mutex);
printf("hello ");
sleep(rand()%3);
printf("world\n");
//sleep(rand()%3);//放在解锁外边,要不主线程不容易抢到cpu
pthread_mutex_unlock(&mutex);
sleep(rand()%3);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread;
srand(time(NULL));
pthread_mutex_init(&mutex,NULL); //子线程创建之前就初始化
pthread_create(&thread,NULL,func,NULL);
while(1)
{
pthread_mutex_lock(&mutex);
printf("HELLO ");
sleep(rand()%3);
printf("WORLD\n");
//sleep(rand()%3);//放在解锁外边,要不子线程不容易抢到cpu
pthread_mutex_unlock(&mutex);
sleep(rand()%3);
}
pthread_mutex_destroy(&mutex);
return 0;
}
共享数据是 stdout,,即printf的打印输出//父线程和子线程都在向标准输出这一文件中写内容,故存在竞争,需要加互斥锁。
注意:访问共享资源前加锁,访问共享资源后立即解锁,锁的粒度应该越小越好,如上所示,
printf这个共享资源使用结束后,立即解锁,另外,sleep放在解锁里边的话,解锁后又继续执行while循环,另一个线程不容易获取cpu,以上两个原因。