同步机制--->互斥锁

      互斥锁指代相互排斥,它是最基本的同步形式。互斥锁用于保护临界区,以保证任何时刻只有一个线程在执行其中的代码,或者任何一个时刻只有一个进程在执行其中的代码。保护一个临界区的代码的通常轮廓大体如下:

     lock_the_mutex(...)

     临界区

     unlock_the_mutex(...)

    Posix互斥锁被声明为具有pthread_mutex_t 数据类型的变量。如果互斥锁变量是静态分配的,那么我们可以把它初始化成常值PTHREAD_MUTEX_INITIALIZER,例如:

    static  pthread_mutex_t   lock = PTHREAD_MUTEX_INITIALIZER

    如果互斥锁是动态分配的(例如通过调用malloc)或者分配在共享内存区中,那么我们必须在运行之时通过调用pthread_mutex_init函数来初始化它。

    下列三个函数给一个互斥锁上锁和解锁。

     #include<pthread.h>

     int  pthread_mutex_lock(pthread_mutex_t *mptr)

     int  pthread_mutex_trylock(pthread_mutex_t * nptr)

     int  pthread_mutex_unlock(pthread_mutex_t  * mptr)

        均返回:若成功返回0,若出错返回正的Exxx值

     如果尝试一个已由另外一个线程锁住的互斥锁上锁,那么pthread_mutex_lock将阻塞到该互斥锁解锁为止。pthread_mutex_trylock是对应的非阻塞函数,如果该互斥锁已锁住,它就返回一个EBUSY错误。

      尽管我们说互斥锁保护的是临界区,实际上保护的是在临界区中被操纵的数据。也就是说,互斥锁通常用于保护由多个线程和多个进程分享的共享数据

       (一、)生产者-消费者问题

     在单个进程中有多个生产者线程和单个消费者线程。整数数组buff含有被生产和消费的条目(也就是共享数据),在第一个例子中,我们只关心多个生产者线程之间的同步。直到所有生产者线程都生产完成工作后,我们才启动消费者线程。

    其代码如下:

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

#define MAXNITEMS 1000
#define MAXNTHREADS 100

int nitems;

struct{
    pthread_mutex_t mutex;
    int buff[MAXNITEMS];
    int nput;
    int nval;
}shared = {PTHREAD_MUTEX_INITIALIZER};

int min(const int a,const int b)
{
    return (a > b ? b : a);
}
void * produce(void *arg)
{
    for(;;)
    {
        pthread_mutex_lock(&shared.mutex);
        if(shared.nput >= nitems)
        {
            pthread_mutex_unlock(&shared.mutex);
            return (NULL);
        }

        shared.buff[shared.nput] = shared.nval;
        shared.nput++;
        shared.nval++;
        pthread_mutex_unlock(&shared.mutex);
        *((int *)arg) += 1;
    }
}


void * consume(void *arg)
{
    int i;
    for(i = 0; i < nitems; ++i)
    {
        #if 0
        if(shared.buff[i] != i)
        {
            printf("buff[%d] = %d\n",i,shared.buff[i]);
        }
        #endif
        printf("buff[%d] = %d\n",i,shared.buff[i]);
    }
    return (NULL);
}

int main(int argc,char*argv[])
{
    int i;
    int nthreads, count[MAXNTHREADS];
    pthread_t tid_produce[MAXNTHREADS],tid_consume;

    if(argc != 3)
    {
        printf("usage: prodcons<# items> <# threads>\n");
        exit(1);
    }

    nitems = min(atoi(argv[1]),MAXNITEMS);
    nthreads = min(atoi(argv[2]),MAXNTHREADS);

    pthread_setconcurrency(nthreads);
    
    for(i = 0; i < nthreads; ++i)
    {
        count[i] = 0;
        pthread_create(&tid_produce[i],NULL,produce,&count[i]);
        sleep(1);
    }

    for(i = 0; i < nthreads; ++i)
    {
        pthread_join(tid_produce[i],NULL);
        printf("count[%d] = %d\n",i,count[i]);
    }

    pthread_create(&tid_consume,NULL,consume,NULL);
    pthread_join(tid_consume,NULL);
    return 0;
}

其执行结果:

  对比上锁与等待:

      现在展示互斥锁用于上锁而不用于等待。我们把上一节的生产者-消费者例子改为在所有生产者线程都启动后立即启动消费者线程。这样在生产者线程产生数据的同时,消费者线程就能处理它,而不是像之前那样,消费者线程直到所有生产者线程都完成后才启动。下面给出具体的代码:

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

#define MAXNITEMS 1000
#define MAXNTHREADS 100

int nitems;

struct{
    pthread_mutex_t mutex;
    int buff[MAXNITEMS];
    int nput;
    int nval;
}shared = {PTHREAD_MUTEX_INITIALIZER};

int min(const int a,const int b)
{
    return (a > b ? b : a);
}
void * produce(void *arg)
{
    for(;;)
    {
        pthread_mutex_lock(&shared.mutex);
        if(shared.nput >= nitems)
        {
            pthread_mutex_unlock(&shared.mutex);
            return (NULL);
        }

        shared.buff[shared.nput] = shared.nval;
        shared.nput++;
        shared.nval++;
        pthread_mutex_unlock(&shared.mutex);
        *((int *)arg) += 1;
    }
}

void consume_wait(int i)
{
    for(;;)
    {
        pthread_mutex_lock(&shared.mutex);
        if(i < shared.nput)
        {
            pthread_mutex_unlock(&shared.mutex);
            return ;
        }
        pthread_mutex_unlock(&shared.mutex);
    }
}



void * consume(void *arg)
{
    int i;
    for(i = 0; i < nitems; ++i)
    {
        consume_wait(i);
        printf("buff[%d] = %d\n",i,shared.buff[i]);
    }
    return (NULL);
}

int main(int argc,char*argv[])
{
    int i;
    int nthreads, count[MAXNTHREADS];
    pthread_t tid_produce[MAXNTHREADS],tid_consume;

    if(argc != 3)
    {
        printf("usage: prodcons<# items> <# threads>\n");
        exit(1);
    }

    nitems = min(atoi(argv[1]),MAXNITEMS);
    nthreads = min(atoi(argv[2]),MAXNTHREADS);

    pthread_setconcurrency(nthreads + 1);
    
    for(i = 0; i < nthreads; ++i)
    {
        count[i] = 0;
        pthread_create(&tid_produce[i],NULL,produce,&count[i]);
        sleep(1);
    }

    pthread_create(&tid_consume,NULL,consume,&count[i]);
    for(i = 0; i < nthreads; ++i)
    {
        pthread_join(tid_produce[i],NULL);
        printf("count[%d] = %d\n",i,count[i]);
    }

    pthread_join(tid_consume,NULL);
    return 0;
}

上述就是对互斥锁的一些基本的应用。。。

包含`pthread_mutex_lock`、`pthread_cond_wait`、`pthread_cond_signal`的读写操作流程图体现了线程同步机制在读写操作中的应用原理,以下是对其原理和含义的详细分析: ### 原理 - **互斥锁(pthread_mutex_lock)**:互斥锁用于保护临界资源,确保同一时间只有一个线程可以访问该资源,避免多个线程同时对共享资源进行读写操作而导致的数据不一致问题。当一个线程调用`pthread_mutex_lock`时,如果互斥锁当前未被锁定,则该线程获得锁并继续执行后续代码;如果互斥锁已被其他线程锁定,则该线程会被阻塞,直到锁被释放。 - **条件变量(pthread_cond_wait)**:条件变量用于线程间的同步,允许线程在某个条件不满足时进入阻塞状态,等待其他线程发出信号。当一个线程调用`pthread_cond_wait`时,它会自动释放持有的互斥锁,并进入阻塞状态。当其他线程调用`pthread_cond_signal`或`pthread_cond_broadcast`唤醒该线程时,它会重新获取互斥锁,并继续执行后续代码。 - **信号通知(pthread_cond_signal)**:`pthread_cond_signal`用于唤醒一个等待在条件变量上的线程。当一个线程调用`pthread_cond_signal`时,它会通知一个正在等待该条件变量的线程,使其从阻塞状态变为就绪状态。被唤醒的线程会在重新获取互斥锁后继续执行后续代码。 ### 含义 - **读写操作流程**:在读写操作流程图中,通常会有多个线程参与,包括读线程和写线程。写线程在进行写操作之前,需要先获取互斥锁,以确保其他线程不能同时进行读写操作。如果写操作的条件不满足,写线程会调用`pthread_cond_wait`进入阻塞状态,等待条件满足。当条件满足时,其他线程会调用`pthread_cond_signal`唤醒写线程,写线程重新获取互斥锁并进行写操作。读线程在进行读操作之前,也需要先获取互斥锁,以确保数据的一致性。如果读操作的条件不满足,读线程也会调用`pthread_cond_wait`进入阻塞状态,等待条件满足。当条件满足时,其他线程会调用`pthread_cond_signal`唤醒读线程,读线程重新获取互斥锁并进行读操作。 - **线程同步**:通过使用互斥锁和条件变量,读写操作流程图实现了线程之间的同步互斥锁确保了同一时间只有一个线程可以访问临界资源,避免了数据竞争问题。条件变量则允许线程在条件不满足时进入阻塞状态,等待其他线程发出信号,从而实现了线程之间的协调和同步。 ### 示例代码 ```c #include <pthread.h> #include <stdio.h> pthread_mutex_t mutex; pthread_cond_t cond; int data_ready = 0; // 写线程函数 void *writer(void *arg) { pthread_mutex_lock(&mutex); // 模拟写操作 data_ready = 1; printf("Writer: Data is ready.\n"); // 唤醒等待的读线程 pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); return NULL; } // 读线程函数 void *reader(void *arg) { pthread_mutex_lock(&mutex); // 等待数据准备好 while (!data_ready) { pthread_cond_wait(&cond, &mutex); } // 模拟读操作 printf("Reader: Data is read.\n"); pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t writer_thread, reader_thread; // 初始化互斥锁和条件变量 pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); // 创建写线程和读线程 pthread_create(&writer_thread, NULL, writer, NULL); pthread_create(&reader_thread, NULL, reader, NULL); // 等待线程结束 pthread_join(writer_thread, NULL); pthread_join(reader_thread, NULL); // 销毁互斥锁和条件变量 pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值