多线程互斥量同步

多线程在一个进程中有多个控制权,也会有多个函数同时运行,执行各自的CPU指令,就算单CPU,也可以通过线程间指令切换,达到多线程同时运行的效果

并发情况,不同线程之间指令无法确定执行先后顺序,假如这个顺序对运行结果造成影响,那么就会出现竞争,而且会出现问题,要解决竞争可以将原本分离的不同线程间的指令修改成原子操作,那么竞争自然就不存在了

多线程由于可以共享资源,因此同步问题必须要解决,在一定时间区间内只能某一个线程访问某一个资源,需要通过一些方法同步资源

下面是一个随手造的例子:

本来主线程和子线程分别对全局变量i进行减操作,但是要求 i 为正数才能减,也就是不管主线程还是子线程,先执行的一方,能减,操作之后 i=0,而接着另一方的if判断为假,就不会再减法操作,最终 i 的值为0;可这里我对于两个线程的减法之前做了一个sleep 1秒,这样能够保证两个线程在if判断的时候都为真,因为来不及减1,这样最终会减两次1,i 的值会变成我们不想得到的-1,这样就很明显暴露了线程同步的问题

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int i = 1;

void *thread_function(){
    if (i > 0){
        sleep(1);
        --i;
    }
    pthread_exit("See you, lihui");
}

int main(){
    pthread_t child;
    if (pthread_create(&child, NULL, thread_function, NULL)){
        printf("Create new thread failed!\n");
        exit(1);
    }
    if (i > 0){
        sleep(1);
        --i;
    }
    pthread_join(child, NULL);
    printf("i = %d\n", i);
    return 0;
}

编译执行一把:

lihui@2015 ~
$ ./a.exe
i = -1

 

互斥量同步

通过使用pthread的互斥接口保护数据,确保同一时间只有一个线程来访问数据;互斥量mutex本质上也就是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成之后释放互斥量上的锁;对互斥量加锁之后,任何其它试图再次对互斥量加锁的线程将会被阻塞,直到当前线程释放掉该互斥锁

如果释放互斥锁的时候有多个线程阻塞了,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变成运行状态的线程可以对互斥锁加锁,其他线程将会看到互斥锁依旧被锁住了,继续阻塞,继续等着变成没锁可用,这样,每次就只有一个线程可以执行

互斥变量用pthread_mutex_t数据类型来表示,在使用之前必须进行初始化,只对静态分配的互斥量可以将它设置为常量PTHREAD_MUTEX_INITIALIZER,也可以调用pthread_mutex_init函数来进行初始化;如果是动态分配互斥量(malloc),那么在释放之前需要调用pthread_mutex_destroy

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                        const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

他们都是成功就返回0,失败就返回错误码;如果互斥量用默认属性,attr设置为NULL

对互斥量加锁,调用pthread_mutex_lock,如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁;对互斥量解锁,调用pthread_mutex_unlock;如果不希望被阻塞,调用pthread_mutex_trylock尝试对互斥量进行加锁,假如此时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则此时互斥量如果已经锁住状态,pthread_mutex_trylock将会失败,不能锁住互斥量

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlick(pthread_mutex_t *mutex);

同样成功返回0,失败返回错误编号

 

因此,有了锁之后,信心满满,由于主线程和子线程最关键的就在于if判断是否为真,判断的结果依赖于对方的减减操作,因此我直接在两个if的前后加上锁和解锁:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int i = 1;
pthread_mutex_t mutex;

void *thread_function(){
    pthread_mutex_lock(&mutex);
    if (i > 0){
        sleep(1);
        --i;
    }
    pthread_mutex_unlock(&mutex);
    pthread_exit("See you, lihui");
}

int main(){
    pthread_t child;
    if (pthread_create(&child, NULL, thread_function, NULL)){
        printf("Create new thread failed!\n");
        exit(1);
    }
    pthread_mutex_lock(&mutex);
    if (i > 0){
        sleep(1);
        --i;
    }
    pthread_mutex_unlock(&mutex);
    pthread_join(child, NULL);
    printf("i = %d\n", i);
    return 0;
}

同一个互斥量加锁,但是出人意料的是结果:

lihui@2015 ~
$ ./a.exe
i = -1

这就神了,让我今晚不要睡觉的节奏??

发表评论