互斥锁的作用
保护共享数据: 在并发机制的情况下,有时候会有多个线程同时访问同一片数据,为了保护数据操作的准确性就需要通过加锁来进行保护。
保持操作互斥: 可能一个程序会有多个操作,但是同一个时间只能有一个操作被执行,例如a/b两个操作,如果a被执行,b就不能被执行,同理b被执行,a就不能执行
操作函数
pthread_mutex_t lock; /* 互斥锁定义 */
pthread_mutex_init(&lock, NULL); /* 动态初始化, 成功返回0,失败返回非0 */
pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化 */
pthread_mutex_lock(&lock); /* 阻塞的锁定互斥锁 */
pthread_mutex_trylock(&thread_mutex);/* 非阻塞的锁定互斥锁,成功获得互斥锁返回0,如果未能获得互斥锁,立即返回一个错误码 */
pthread_mutex_unlock(&lock); /* 解锁互斥锁 */
pthread_mutex_destroy(&lock) /* 销毁互斥锁 */
实例:保护共享数据
第一个作用是保护共享的数据,在说明之前先看一下没有互斥锁保护的情况下,会发生什么样的状况,例子如下:
#include
#include
#include
#include
#include
static int g_count = 0;
static void *thread_fun_1(void *data)
{
g_count++;
printf("%s g_count: %d\n", __func__, g_count);
}
static void *thread_fun_2(void *data)
{
g_count++;
printf("%s g_count: %d\n", __func__, g_count);
}
static void *thread_fun_3(void *data)
{
g_count++;
printf("%s g_count: %d\n", __func__, g_count);
}
int main(int argc, char const *argv[])
{
pthread_t pid[3];
pthread_create(&pid[0], NULL, thread_fun_1, NULL);
pthread_create(&pid[1], NULL, thread_fun_2, NULL);
pthread_create(&pid[2], NULL, thread_fun_3, NULL);
pthread_join(pid[0], NULL);
pthread_join(pid[1], NULL);
pthread_join(pid[2], NULL);
return 0;
}
3个线程都会对g_count进行操作,没有任何保护的情况下,3个线程是存在竞争关系的,所以g_count最终可能会是1、2、3三种值中的一种,运行结果如下
下面开始对数据进行保护,例子如下:
#include
#include
#include
#include
#include
static pthread_mutex_t g_mutex_lock;
static int g_count = 0;
static void *thread_fun_1(void *data)
{
pthread_mutex_lock(&g_mutex_lock);
g_count++;
printf("%s g_count: %d\n", __func__, g_count);
pthread_mutex_unlock(&g_mutex_lock);
}
static void *thread_fun_2(void *data)
{
pthread_mutex_lock(&g_mutex_lock);
g_count++;
printf("%s g_count: %d\n", __func__, g_count);
pthread_mutex_unlock(&g_mutex_lock);
}
static void *thread_fun_3(void *data)
{
pthread_mutex_lock(&g_mutex_lock);
g_count++;
printf("%s g_count: %d\n", __func__, g_count);
pthread_mutex_unlock(&g_mutex_lock);
}
int main(int argc, char const *argv[])
{
int ret;
pthread_t pid[3];
ret = pthread_mutex_init(&g_mutex_lock, NULL);
if (ret != 0) {
printf("mutex init failed\n");
return -1;
}
pthread_create(&pid[0], NULL, thread_fun_1, NULL);
pthread_create(&pid[1], NULL, thread_fun_2, NULL);
pthread_create(&pid[2], NULL, thread_fun_3, NULL);
pthread_join(pid[0], NULL);
pthread_join(pid[1], NULL);
pthread_join(pid[2], NULL);
pthread_mutex_destroy(&g_mutex_lock);
return 0;
}
对数据g_count的操作进行加锁之后,同一个时间只有一个线程能获取到锁,也就是只有一个线程能对g_count进行操作,保证了g_count的数据的准确性
实例:保持操作的互斥性
有些情况下,2个不同的操作是不能同时进行的,例如fingerprint中的enroll和verify同一时间只能有一个操作进行。保持操作的互斥性本质上其实还是在保护共有的数据。看下下面的例子,打印hello的时候,world是无法打印的,如果希望打印world只能等待打印hello的线程退出之后再打印
#include
#include
#include
#include
#include
static pthread_mutex_t g_mutex_lock;
static void *thread_fun_1(void *data)
{
pthread_mutex_lock(&g_mutex_lock);
int i = 0;
while (i < 10) {
printf("hello\n");
i++;
sleep(1);
}
pthread_mutex_unlock(&g_mutex_lock);
}
static void *thread_fun_2(void *data)
{
pthread_mutex_lock(&g_mutex_lock);
int i = 0;
while (i < 10) {
printf("world\n");
i++;
sleep(1);
}
pthread_mutex_unlock(&g_mutex_lock);
}
static void do_print_hello()
{
pthread_t pth_id;
int result = pthread_create(&pth_id, NULL, thread_fun_1, NULL);
}
static void do_print_world()
{
pthread_t pth_id;
int result = pthread_create(&pth_id, NULL, thread_fun_2, NULL);
}
int main(int argc, char const *argv[])
{
int ret;
int cid;
ret = pthread_mutex_init(&g_mutex_lock, NULL);
if (ret != 0) {
printf("mutex init failed\n");
return -1;
}
while (1) {
scanf("%d", &cid);
getchar();
switch (cid) {
case 0:
do_print_hello();
break;
case 1:
do_print_world();
break;
default:
break;
}
}
pthread_mutex_destroy(&g_mutex_lock);
return 0;
}
上面的互斥锁是阻塞式的锁,也可以通过非阻塞式的锁进行,看下面的例子,pthread_mutex_trylock()函数如果获取到互斥锁了,会返回0,如果没有获取的互斥锁,会立即返回一个非0值,例子中通过g_cancel来通知线程进行退出,如果当前正在打印hello,发出打印world命令之后,通过pthread_mutex_trylock()就能知道当前有没有打印线程正在运行,如果有在运行的线程,通过置位g_cancel来退出正在运行的线程
#include
#include
#include
#include
#include
static pthread_mutex_t g_mutex_lock;
static int g_cancel = 0;
static void *thread_fun_1(void *data)
{
if (pthread_mutex_trylock(&g_mutex_lock) != 0) {
g_cancel = 1;
return 0;
}
int i = 0;
g_cancel = 0;
while (i < 10) {
if (g_cancel) break;
printf("hello\n");
i++;
sleep(1);
}
pthread_mutex_unlock(&g_mutex_lock);
}
static void *thread_fun_2(void *data)
{
if (pthread_mutex_trylock(&g_mutex_lock) != 0) {
g_cancel = 1;
return 0;
}
int i = 0;
g_cancel = 0;
while (i < 10) {
if (g_cancel) break;
printf("world\n");
i++;
sleep(1);
}
pthread_mutex_unlock(&g_mutex_lock);
}
static void do_print_hello()
{
pthread_t pth_id;
int result = pthread_create(&pth_id, NULL, thread_fun_1, NULL);
}
static void do_print_world()
{
pthread_t pth_id;
int result = pthread_create(&pth_id, NULL, thread_fun_2, NULL);
}
int main(int argc, char const *argv[])
{
int ret;
int cid;
ret = pthread_mutex_init(&g_mutex_lock, NULL);
if (ret != 0) {
printf("mutex init failed\n");
return -1;
}
while (1) {
scanf("%d", &cid);
getchar();
switch (cid) {
case 0:
do_print_hello();
break;
case 1:
do_print_world();
break;
default:
break;
}
}
pthread_mutex_destroy(&g_mutex_lock);
return 0;
}