linux线程同步(队列方式)

看了一些关于信号量的线程同步方式,今天用了一下。

我对于线程同步一直有疑问,在主线程和子线程处理时间不相同的时候,用这种信号量,如何保证同步。

假如主线程比较快,信号量连加了n个,但是子线程就不断减这个n,减到0。但是如果主线程太快太快,需要停一停,比如缓冲区快溢出了,主线程需要挂起。

由什么来唤醒主线程呢?子线程?不过这样的话,容易造成主线程死锁,或者主和子都卡死。

下面的程序,没有用到信号量同步,信号量只是负责开启子线程而已。主要是队列的实现而已。等我把上面的问题解决完会写上更新的程序。

队列头文件:

#ifndef _queue_H
#define _queue_H

typedef int T;
typedef struct queue Queue;
struct queue{
    T data;
    Queue* next;
};

static int length=0;

Queue* CreateQueue();
void DestroyQueue(Queue* h);
int Push(Queue* h,T t);
Queue* Pop(Queue* h);
void Print(Queue* h);


#endif

队列c文件:

#include <stdio.h>
#include <stdlib.h>
#include "queue.h"

const int MaxSize=50;

Queue* CreateQueue()
{
    Queue* h=(Queue*)malloc(sizeof(Queue));
    if(h==NULL)
    {
        printf("Malloc failed!
");
        return NULL;
    }
    h->next=NULL;
    return h; 
}

void DestroyQueue(Queue* h)
{
    Queue* p=h;
    while(p!=NULL)
    {
        Queue* tmp=p;
        p=p->next;
        free(tmp);
    }
}

int Push(Queue* h,T t)
{
    Queue* tmp=(Queue*)malloc(sizeof(Queue));
    if(tmp==NULL)
    {
        printf("Malloc failed!
");
        return;
    }
    tmp->data=t;
    tmp->next=NULL;
    
    if(length>=MaxSize)
    {
        printf("Queue is full!
");
        return -1;
    }
    Queue* p=h;
    while(p->next!=NULL)
    {
        p=p->next;
    }
    p->next=tmp;
    length++;
}

Queue* Pop(Queue* h)
{
    Queue* res=h->next;
    if(h->next==NULL)
    {
        //printf("Queue is empty!
");
        return NULL;
    }
    h->next=h->next->next;
    length--;
    return res;
}

void Print(Queue* h)
{
    Queue* p=h->next;
    while(p!=NULL)
    {
        printf("%d
",p->data);
        p=p->next;
    }
    printf("Above is the Queue!
");
    printf("
");
}

主程序:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include "queue.h"

/*
此程序模仿生产者-消费者模型
生产者:0-99的int型数,进入队列。如果队列(默认最大50)满了,提示已满,主线程等待直到队列可以进入。
消费者:对生产者产生的数据计算并输出10个结果。如果队列空了,提示已空。
通过队列和信号量来同步。
此程序以主线程为主,因为相互发信号会导致死锁,程序卡住!应该是技术不够,不过也至于冒这个险!
*/

sem_t btn_sem;//二进制信号量
sem_t flag_sem;
Queue *head;//队列头部
int End=0;//主线程结束标志位

//子线程执行函数
void *thread_func(void *arg)
{
    int i=0; 
    sem_wait(&btn_sem);//子线程等待开始
    while(1)
    {    
        Queue *tmp=Pop(head);
        if(tmp==NULL)//队列已经空了
        {
        }
        else
        {
            printf("Chlid Thread! ");
            for(i=tmp->data;i<tmp->data+10;i++)
            {
                printf("%d ",i);
            }
            printf("
");//子线程输出
            free(tmp);
        }
        if(End==1)
        {
            if(head->next==NULL)
                return;
        }
    }    
}

int main()
{
    head=CreateQueue();
    int i=0;
    char aa[]="aaaa!";
    pthread_t a_thread;

    int res;
    res=sem_init(&btn_sem,0,0);
    if(res!=0)
    {
        printf("Signal initials failed!
");
        return;
    }
    res=sem_init(&flag_sem,0,0);
    if(res!=0)
    {
        printf("Signal initials failed!
");
        return;
    }
    res=pthread_create(&a_thread,NULL,thread_func,(void*)aa);
    if(res!=0)
    {
        printf("Create thread failed!
");
        return;
    }

    sem_post(&btn_sem);//第一次进入主循环时发信号,告知子线程可以开始
    //主线程,生产者
    for(i=0;i<100;i++)
    {
        res=Push(head,i);//数据直接进入队列
        if(res==-1)//如果队列满了,需要等待
        {
            while(Push(head,i)==-1);//放到队列有空间为止
            //Push(head,i);//子线程发信号,表示队列有空间了,此时把等待的信息push进队列
        }
        printf("%d
",i);
    }
    End=1;

    void *thread_res;
    pthread_join(a_thread,&thread_res);//等待子线程结束后,结束主线程
    sem_destroy(&btn_sem);
    sem_destroy(&flag_sem);

    DestroyQueue(head);
    return 0;
}

makefile:

main:main.o queue.o
    gcc -o main main.o queue.o -pthread

main.o:main.c queue.h
    gcc -c main.c -pthread

queue.o:queue.c queue.h
    gcc -c queue.c

如果队列满了,这里不采用等待子线程做,而是不断访问队列,等待队列有空间,其实也就是等待子线程操作。

不过这种方式不好!

因为主线程其实还是一直在做自己的事情,CPU并不知道此时需要少分时间片给主线程,而是按照正常的分配给主和子。

理想情况,此时应该主线程挂起,主要给子线程,让其运行,产生多余的队列空间。

所以以上程序的效率是不高的。

原文地址:https://www.cnblogs.com/wyc199288/p/5475478.html