Linux中的同步技术之互斥量

互斥量从本质上说就像是一把锁,提供资源的保护访问,互斥量有两种状态,锁住(lock)与解锁状态(unlock),用来保证一段时间内只有一个线程使用该共享资源。

互斥量的数据类型为pthread_mutex_t,如果互斥锁变量是静态分配的,那么一般将其初始化为常值PTHREAD_MUTEX_INITIALIZER, 如:static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

r若互斥锁变量是动态分配的(如调用malloc函数)或者说是该互斥锁分配在共享内存区中,则需要用初始化函数
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
来初始化该互斥锁变量。

互斥锁保护的是临界区,但实际上其保护的临界区中的被操纵的数据,即就是说,互斥锁通常用于保护由多个线程或多个进程分享的共享数据。

互斥锁的上锁与解锁函数:
int pthread_mutex_lock(pthread_mutex_t *mutex);
该函数是互斥锁的上锁(加锁)函数,成功返回0,失败返回错误编号,使用该函数时如果一个互斥锁已经被某个线程加锁锁住,调用该函数就会阻塞调用线程直到该互斥锁解锁为止。参数mutex是互斥锁变量。

int pthread_mutex_trylock(pthread_mutex_t *mutex);
该函数是互斥锁的尝试上锁函数,成功返回0,失败返回错误编号。该函数是一个非阻塞函数(就是不阻塞),即就是若一个互斥锁变量已经已经被别的线程加锁,则调用该函数的线程尝试加锁,则其加锁不成功,但是其不会阻塞该线程,而是继续向下执行。若一个互斥锁未被加锁,则其就会加锁成功。
参数mutex是互斥锁变量。

int pthread_mutex_unlock(pthread_mutex_t *mutex);
该函数是互斥锁的解锁函数,成功返回0,失败返回错误编号。只要临界区的操作执行完成,就解锁互斥量。参数mutex是互斥锁变量。

互斥锁变量的初始化和销毁函数:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
该函数是互斥量的销毁函数,参数为互斥量。成功返回0.失败返回错误编号。

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
该函数是互斥量的初始化函数,mutex参数为互斥量,attr为互斥量的属性。成功返回0,失败返回错误编号。该函数用于动态分配或是在共享内存区定义的互斥量的初始化。

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
这是互斥量的静态初始化方法,用于静态初始化的互斥量。

以下是一个使用互斥量同步的生产者和消费者程序:

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

#define BUFFER_SIZE 6
#define MAX_SIZE    50

struct{
    pthread_mutex_t mutex;
    pthread_cond_t  nofull;
    pthread_cond_t  noempty;

    int buff[MAX_SIZE];
    int nput;  //存放数据的缓冲区下标
    int nval;  //存放的数据值
    int count; //缓冲区值中的存放的数据个数
}shared = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER};




//生产者程序
void *produce(void *arg)
{
    for( ; ; ){
        pthread_mutex_lock(&shared.mutex);    //加锁从此行至pthread_mutex_unlock形成临界区
        if(shared.nval >= MAX_SIZE){          //若生产者产生的数据值>=最大值就解锁退出
            pthread_mutex_unlock(&shared.mutex);
            printf("hjhfrkfhrkjfhrkfr\n");
            break;
        }
        if(shared.count < BUFFER_SIZE){       //若缓冲区中存放的数据个数小于缓冲区所能存放的个数
            shared.buff[shared.nput] = shared.nval;   //就继续在缓冲区中存入数据
            shared.nput++;     //改变缓冲区中存放的数据的下标
            if(shared.nput >= BUFFER_SIZE){   //当缓冲区的数据的下标>=缓冲的大小时,又开始重新计数
                shared.nput = 0;
            }
            shared.nval++;    //生产者产生的数据值增加
            shared.count++;   //生产者产生的数据个数增加
            pthread_cond_signal(&shared.noempty);    //给消费者线程发送信号,告诉消费者线程缓冲区中有数据可读
        }else{     //缓冲区空间不足,无法存入数据,使生产者线程处于阻塞状态
            pthread_cond_wait(&shared.nofull, &shared.mutex);     //阻塞生产者线程,等待消费者线程给其发送缓冲区不满的信号
        }
        pthread_mutex_unlock(&shared.mutex);   //解锁互斥量
    }
}



//消费者程序
void *consume(void *arg)
{
    int j = 0;   //缓冲区的计数器,用来读出缓冲区中的数据
    //遍历输出读出的数据
    for(int i = 0; i < MAX_SIZE; ){
        pthread_mutex_lock(&shared.mutex);   //加锁互斥量至pthread_mutex_unlock形成临界区
        if(shared.count > 0){   //若缓冲区中的数据个数>0就将其中的数据读出
            printf("value = %d\n", shared.buff[j]);
            //sleep(1);
            j++;   //缓冲区的数据下标,后移读出下一个位置的数据
            if(j >= BUFFER_SIZE){   //若该计数下标>=缓冲区的大小,就重新从下标0开始重新计数
                j = 0;
            }
            shared.count--;   //缓冲区中的元素个数减一
            pthread_cond_signal(&shared.nofull);    //给生产者线程发送信号,告诉生产者线程缓冲区不满,可以存入数据
            i++;    //计数器,用来计数该读出的数据的个数
        }else{   //缓冲区中没有数据可以读出,就阻塞等待缓冲区中填入数据
            pthread_cond_wait(&shared.noempty, &shared.mutex);  //阻塞等待生产者线程发送缓冲区不空的信号
        }
        pthread_mutex_unlock(&shared.mutex);   //解锁互斥锁
    }
}

int main()
{
    pthread_t ptid;
    pthread_t ctid;

    pthread_create(&ptid, NULL, produce, NULL);    //创建生产者线程
    pthread_create(&ctid, NULL, consume, NULL);    //创建消费者线程

    pthread_join(ptid, NULL);    //终止生产者线程
    pthread_join(ctid, NULL);    //终止消费者线程
    return 0;
}

你可能感兴趣的:(线程同步,互斥量,同步技术)