【Linux】线程的互斥与同步!!

在系统里面线程的资源不同于进程的资源,进程在系统里面是一个一个的单独个体,但是线程的资源大部分是共享的,所以在线程访问问一个临界资源的时候需要同步与互斥机制,从而保证在多线程的程序里main不会出现问题。

模拟实现售票系统:

int tacket = 100;
void* sell_tacket(void* arg){
    while(1){
        if(tacket > 0){
            usleep(10000);
            printf("%u id sell tacket :%d\n",pthread_self(),tacket);
            tacket--;
        }
        else
            break;
    }
}
int main(){
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,sell_tacket,NULL);
    pthread_create(&tid2,NULL,sell_tacket,NULL);
    pthread_create(&tid3,NULL,sell_tacket,NULL);
    pthread_create(&tid4,NULL,sell_tacket,NULL);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

逻辑上面没有问题,但是运行结果不对。
【Linux】线程的互斥与同步!!_第1张图片

原因是在每个线程在sleep时候很有可能被切换出去,那么其他线程就会执行自己的代码,所以在到最后面的时候会出现票数为负数的情况。


互斥量
类型:pthread_mutex_t :定义互斥量可以pthread_mutex_t lock;
互斥量的初始化:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
或者
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

使用PTHREAD_MUTEX_INITIALIZER初始化互斥量一般是初始化全局的互斥量。

互斥量的销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex:已初始化的互斥量
注:

  • 使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁
  • 不能销毁一个加锁的互斥量
  • 已经销毁的互斥量,需要确保后面的线程再尝试加锁

加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

所以售票系统可以改进为:


pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

int tacket = 100;
void* sell_tacket(void* arg){
    pthread_mutex_lock(&lock);
    while(1){
        if(tacket > 0){
            usleep(10000);
            printf("%u id sell tacket :%d\n",pthread_self(),tacket);
            tacket--;
        }
        else{
            pthread_mutex_unlock(&lock);
            break;
        }
    }
    pthread_mutex_unlock(&lock);
}


int main(){
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,sell_tacket,NULL);
    pthread_create(&tid2,NULL,sell_tacket,NULL);
    pthread_create(&tid3,NULL,sell_tacket,NULL);
    pthread_create(&tid4,NULL,sell_tacket,NULL);


    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

【Linux】线程的互斥与同步!!_第2张图片

但是这里有一个缺点就是,在有一个线程再购票的时候其他的线程是无法取得购票权的。


条件变量
条件变量的类型:pthread_cond_t 定义条件变量可以:pthread_cond_t cond

条件变量的初始化:
int pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t *attr);
或者
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

条件变量的销毁:
int pthread_cond_destroy(pthread_cond_t *cond);

唤醒等待:
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒多个
int pthread_cond_signal(pthread_cond_t *cond);//唤醒一个

改进售票系统:一个线程只能够买一张票

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond;


int tacket = 100;
void* sell_tacket(void* arg){
    pthread_mutex_lock(&lock);
        if(tacket > 0){
            pthread_cond_wait(&cond,&lock);
            printf("%u id sell tacket :%d\n",pthread_self(),tacket);
            tacket--;
        }
    pthread_mutex_unlock(&lock);
}


int main(){
    int i = 0;
    int j = 0;
    pthread_t tid1,tid2,tid3,tid4;
        pthread_cond_init(&cond,NULL);
    pthread_create(&tid1,NULL,sell_tacket,NULL);
    pthread_create(&tid2,NULL,sell_tacket,NULL);
    pthread_create(&tid3,NULL,sell_tacket,NULL);
    pthread_create(&tid4,NULL,sell_tacket,NULL);
    sleep(1);
    for(;i<4;i++){
            usleep(1000);
            pthread_cond_signal(&cond);
    }

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

【Linux】线程的互斥与同步!!_第3张图片

注意:
由于解锁和等待不是原子操作,所以在解锁之后到等待之前有其他的线程满足条件,发送了信号,这个时候原线程将会错过这个信号,那么有可能该线程将一直等待下去。
在使用条件变量的时候应该保证解锁和等待是一个原子操作。

你可能感兴趣的:(Linux)