多线程同步的相关问题

一、 生产者消费者问题
生产者消费者共享缓冲区,生产者向缓冲区中放数据,消费者从缓冲取中取数据,当缓冲区中被放满时,生产者进程就必须进入挂起状态,直到消费者从缓冲中取走数据时,生产者才能继续向缓冲区中存放数据,同样当缓冲取中没有数据时,消费者进程就必须进入挂起休眠状态,直到生产者向缓冲区中放入数据时,消费者才能被唤醒继续从缓冲区中取走数据。

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。
————————————————
采用互斥锁实现,线程间同步

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#define bufsize 10 //设置缓存区大小
typedef struct node
{
    int val;
    struct node *next;
} node;
int count = 0;
node *head = NULL;
pthread_mutex_t point = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t full = PTHREAD_COND_INITIALIZER;   
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;
void *producer()
{
    node *t;
    while (1)
    {
        pthread_mutex_lock(&point);
        while (count >= bufsize)
            pthread_cond_wait(&full, &point);
        t = (node *)malloc(sizeof(node));
        t->val = rand() % 100 + 1;
        count++;
        t->next = head;
        head = t;
        printf("--producer %d count %d \n", t->val, count);
        pthread_mutex_unlock(&point);
        pthread_cond_signal(&empty);
        sleep(rand() % 2);
    }
}
void *consumer()
{
    node *t;
    while (1)
    {
        pthread_mutex_lock(&point);
        while (count == 0)
            pthread_cond_wait(&empty, &point);
        t = head;
        head = head->next;
        printf("--consumer %d count %d\n", t->val, count);
        free(t);
        count--;
        pthread_mutex_unlock(&point);
        pthread_cond_signal(&full);
        sleep(rand() % 5);
    }
}
int main()
{
    pthread_t cid, pid;
    srand(time(NULL));
    if (pthread_create(&pid, NULL, producer, NULL) != 0)
    {
        perror("pthread_creat cid error:");
        exit(1);
    }
    if (pthread_create(&cid, NULL, consumer, NULL) != 0)
    {
        perror("pthread_creat pid error:");
        exit(1);
    }
    pthread_join(pid, NULL);
    pthread_join(cid, NULL);
    return 0;
}

二、 哲学家问题
有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了。
思路:采用信号量保持线程同步/互斥,临界区访问问题以及避免死锁。

方法1:同时让4个哲学家进行竞争5个筷子,避免死锁

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#define n 5 //五个哲学家
sem_t a[n]; //n个信号量代表筷子
sem_t t;
void *ptr1(void *arg) //方法1:同时让4个哲学家进行竞争5个筷子,避免死锁
{
    int i = (*(int *)arg);
    while (1)
    {
        printf("哲学家%d思考\n", i);
        sem_wait(&t);
        printf("哲学家%d饿了\n", i);
        sem_wait(&a[i]);
        printf("哲学家%d拿起了%d筷子,一根不能吃\n", i, i);
        sem_wait(&a[(i + 1) % n]);
        printf("哲学家%d拿起了%d %d筷子,开始吃饭\n", i, i, (i + 1) % n);
        sleep(3);
        sem_post(&a[i]);
        sem_post(&(a[(i + 1) % n]));
        sem_post(&t);
    }
}
int main()
{
    pthread_t b[5];
    sem_init(&t, 0, 4); //设置信号量为4,即同时只有4位发生竞争
    for (int i = 0; i < 5; i++)
    {
        sem_init(&a[i], 0, 1);
        pthread_create(&b[i], NULL, ptr1, &i);
    }
    for (int i = 0; i < n; i++)
    {
        pthread_join(b[i], NULL);
    }
    for (int i = 0; i < n; i++)
    {
        sem_destroy(&a[i]);
    }
    return 0;
}

方法2:同时让4个哲学家进行竞争5个筷子,避免死锁

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#define n 5 //五个哲学家
sem_t a[n]; //n个信号量代表筷子
sem_t t;
void *ptr2(*arg) //方法2:对于一个哲学家要就餐,如果能同时拿到2根筷子就餐,
{                //如果不行,就释放所有筷子
    int i = (*(int *)arg);
    while (1)
    {
        if(sem_trywait(&a[i])==-1)
        continue;
        printf("哲学家%d拿起了%d筷子,一根不能吃\n", i, i);
        sleep(rand()%3);
        if (sem_trywait(&a[(i+1)%n])==-1)
        {
            sem_post(&a[i]);
            continue;
        }
        printf("哲学家%d拿起了%d %d筷子,开始吃饭\n", i, i, (i + 1) % n);
        sleep(rand()%3);
        sem_post(&a[i]);
        sem_post(&(a[(i + 1) % n]));
    }
}
int main()
{
    pthread_t b[5];
    srand(time(NULL));
    sem_init(&t, 0, 4); //设置信号量为4,即同时只有4位发生竞争
    for (int i = 0; i < 5; i++)
    {
        sem_init(&a[i], 0, 1);
        pthread_create(&b[i], NULL, ptr2, &i);
    }
    for (int i = 0; i < n; i++)
    {
        pthread_join(b[i], NULL);
    }
    for (int i = 0; i < n; i++)
    {
        sem_destroy(&a[i]);
    }
    return 0;
}

/

方法3:对于奇数先左后右,偶数相反

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#define n 5 //五个哲学家
sem_t a[n]; //n个信号量代表筷子
sem_t t;
pthread_mutex_t ltck;
void *ptr2(*arg) //方法3:对于奇数先左后右,偶数相反
{   
    int i = (*(int *)arg);
    while (1)
    {
        printf("哲学家%d思考\n", i);
        printf("哲学家%d饿了\n", i);
        if (i % 2 == 0)
        {
            sem_wait(&a[i]);
            printf("哲学家%d拿起了%d筷子,一根不能吃\n", i, i);
            sem_wait(&a[(i + 1) % n]);
            printf("哲学家%d拿起了%d %d筷子,开始吃饭\n", i, i, (i + 1) % n);
        }
        else
        {
            sem_wait(&a[(i + 1) % n]);
            printf("哲学家%d拿起了%d筷子,一根不能吃\n", i, (i + 1) % n);
            sem_wait(&a[i]);
            printf("哲学家%d拿起了%d %d筷子,开始吃饭\n", i, (i + 1) % n, i);
        }
        sleep(rand()%3);
        sem_post(&a[i]);
        sem_post(&a[(i + 1) % n]);
    }
}
int main()
{
    pthread_t b[5];
    pthread_mutex_init(&ltck,NULL);
    srand(time(NULL));
    sem_init(&t, 0, 4); //设置信号量为4,即同时只有4位发生竞争
    for (int i = 0; i < 5; i++)
    {
        sem_init(&a[i], 0, 1); 
        pthread_create(&b[i], NULL, ptr2, &i);
    }
    for (int i = 0; i < n; i++)
    {
        pthread_join(b[i], NULL);
    }
    for (int i = 0; i < n; i++)
    {
        sem_destroy(&a[i]);
    }
    return 0;
}
上一篇:Linux 多线程应用 降低内存(VSZ)占用(嵌入式)


下一篇:Linux下pthread_once()函数