Linux C 互斥锁的使用

互斥锁的作用
保护共享数据: 在并发机制的情况下,有时候会有多个线程同时访问同一片数据,为了保护数据操作的准确性就需要通过加锁来进行保护。
保持操作互斥: 可能一个程序会有多个操作,但是同一个时间只能有一个操作被执行,例如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三种值中的一种,运行结果如下
Linux C 互斥锁的使用_第1张图片
下面开始对数据进行保护,例子如下:

#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;
}

你可能感兴趣的:(C)