二元信号量提供了一种很方便的方法来确保对共享变量的互斥访问,即每个共享变量与一个信号量 s(初始为1)联系起来,然后P(s)和V(s)操作将相应的临界区包围起来。互斥锁是以提供互斥为目的的二元信号量,二者均属于挂起等待锁。
互斥锁(针对于线程)
互斥锁用于确保同一时间只有一个线程能访问被互斥锁保护的资源(互斥锁是针对于线程的)。锁定互斥量的线程与解锁互斥量的线程必须是同一个线程。
互斥锁的引入必须对互斥量mutex有一个新的认识,多个线程同时访问共享数据时可能会发生冲突,这与信号的可重入性是同样的问题。比如两个线程要把某个全局变量加1,这个操作在Linux下需要三条指令完成:
(1)从内存读变量到寄存器;
(2)寄存器值加1;
(3)将寄存器的值写回内存。
我们在读取变量的值和把变量的新值保存回去两步操作之间插入一个 printf 调用,它会执行 write 系统调用进内核,为内核调用别的线程执行提供一个很好的时机(线程与进程间切换最好的时机:从内核态切换到用户态)。我们在一个循环中重复上述操作几千次,就会出现访问冲突的现象。
#include<stdio.h>
#include<pthread.h>
#include<sys/types.h>
#include<unistd.h>
int count = 0;
void *pthread_run(void* arg)
{
int val = 0;
int i = 0;
while(i < 5000)
{
i++;
val = count;
printf("pthread:%lu,count:%d\n",pthread_self(),count);
count = val + 1;
}
return NULL;
}
int main()
{
pthread_t pth1;
pthread_t pth2;
pthread_create(&pth1, NULL, &pthread_run, NULL);
pthread_create(&pth2, NULL, &pthread_run, NULL);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
printf("count:%d\n",count);
return 0;
}
运行结果如下图所示: