线程(进程)的同步与互斥实例

1.有一个队列,线程1负责从网络接收分组,并将收到的分组放到队列尾,然后再次从网络中接收下一个到达的分组,并进行同样的队列操作。线程2从此队列头中取出一个分组进行处理,处理完毕后,再次从队列中取出一个分组进行处理,处理完毕后再次从队列头取出下一个分组进行处理,两个线程都以无限循环的方式工作,因此该队列为临时资源,若队列不为空,线程2才应该开始循环处理,否则需要等待,显然如果线程2不停的检测队列是否为空,虽然能够正常工作,但是很消耗cpu资源,cpu效率低,当队列为空时,线程2进入休眠状态,当队列不为空时,线程2自动被唤醒,可以利用条件变量解决这个问题。

 

函数说明:

 

#include <pthread.h>
int  pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

返回值:0代表成功,错误号代表失败

 

参数mutex对条件句进行保护,线程2自动把加锁的互斥量传给此函数,函数把线程2放到等待条件的线程列表上,然后对互斥量进行自动解锁,这两步操作属于原子操作,这样就不会造成条件检查和线程进入休眠状态等待之间有其它线程进入的问题,当线程2因条件不满足而进入休眠后,因为互斥量已经被解锁,于是线程1就可以加锁互斥量并将收到的分组放到队列尾,然后主动解锁互斥量,接着线程1再调用下面的pthread_cond_signal函数通知线程2条件已经发生变化。

 

#include <pthread.h>
int  pthread_cond_signal(pthread_cond_t *cond);


返回值:0代表成功,错误号代表失败。

 

由于此条件发生了改变,则pthread_cond_wait函数返回,保护条件的互斥量将再次被自动加锁(注意这里是自动加锁,不需要线程2主动加锁),线程2被唤醒,于是就可以对临界资源进行操作,即线程2从队列中取出下一个分组并主动对互斥量进行解锁,之后再对该分组进行后续的处理。
 

include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>

#include <pthread.h>

#include <queue>

#include <time.h>

std::queue<int>q;

//静态初始化互斥量

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

//静态初始化条件变量

pthread_cond_t condition=PTHREAD_COND_INITIALIZER;

void print(int val)

{

    printf("获取一个新数据:%d\n",val);

}

void *thread1(void *arg)

{

    srand(time(NULL));

    while (1)

    {

        pthread_mutex_lock(&mutex);//加锁互斥量

        q.push(rand()%100+1);      //向队列中插入数据

        printf("插入一个新数据\n");

        pthread_mutex_unlock(&mutex);//解锁互斥量

       //通知线程2条件发生改变

        pthread_cond_signal(&condition);

        sleep(1);

    }

}

void *thread2(void *arg)

{

    srand(time(NULL));

    while (1)

    {

        pthread_mutex_lock(&mutex);//加锁互斥量

       //若队列为空,则休眠等待条件改变

        while (q.empty())

            pthread_cond_wait(&condition, &mutex);

        

       //若队列不为空,则从队列中取出一个数据

        int val=q.front();

        q.pop();

        pthread_mutex_unlock(&mutex);//解锁互斥量

        //处理数据

        print(val);

        sleep(1);

        

    }

}

int main()

{

    pthread_t pit1;  //线程1标志符

    pthread_t pit2;  //线程2标识符

    int err=pthread_create(&pit1,NULL,thread1,NULL);//创建线程1

    if(err!=0)

    {

        perror("creat thread1 error\n");

        exit(1);

    }

    err=pthread_create(&pit2,  NULL, thread2,  NULL);  //创建线程2

    if(err!=0)

    {

        perror("creat thread2 error\n");

        exit(1);

    }

    while (1)

    {}

    return 0;

}

2.用两个线程实现如下功能:主线程负责读取数据,当遇到特殊字符"end"后退出程序,另一个线程负责处理这些输入的数据(输出其字符串长度)。

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>

#include <pthread.h>

#include <time.h>

#include <semaphore.h>

#define WORK_SIZE 1024  //缓冲区大小

pthread_mutex_t work_mutex;  //互斥量

int time_to_exit=0;   //退出标记变量

char work_area[WORK_SIZE]; //缓冲区

void *thread_function(void *arg)//线程功能函数

{

    sleep(1);

    pthread_mutex_lock(&work_mutex);//加锁,保证每次输入后都能进入该线程处理

    while (strncmp("end",work_area,3)!=0)//判断是否结束

    {

        printf("You input %d characters\n",strlen(work_area)-1);//输出字符串长度

        work_area[0]='\0';//相当于清空缓冲区

        pthread_mutex_unlock(&work_mutex);//解锁互斥量

        sleep(1);

        

       //判断是否在主线程执行前又再次执行该线程

        pthread_mutex_lock(&work_mutex);

        while (work_area[0]=='\0')

        {

            pthread_mutex_unlock(&work_mutex);

            sleep(1);

            pthread_mutex_lock(&work_mutex);

        }

    }

    time_to_exit=1;//更新退出标记

    work_area[0]='\0';

    pthread_mutex_unlock(&work_mutex);

    pthread_exit(0);//线程退出

}

int main()

{

   

    int res;

    pthread_t a_thread;    //线程标志符

    void *thread_result;

    res=pthread_mutex_init(&work_mutex,NULL);//初始化互斥量

    if(res!=0)

    {

        perror("Mutex initialization failed");

        exit(EXIT_FAILURE);

    }

    res=pthread_create(&a_thread,  NULL,thread_function,  NULL);

    if (res!=0)

    {

        perror("Thread creation failed");

        exit(EXIT_FAILURE);

    }

    

    pthread_mutex_lock(&work_mutex);//加锁保证输出和输入的原子性

    printf("Input some text,Enter 'end'to finish\n");

    while (!time_to_exit)

    {

        fgets(work_area,WORK_SIZE,stdin);

       //输入成功后即可解锁

        pthread_mutex_unlock(&work_mutex);

        

       //避免在功能线程未执行前再次调用主线程,这里需要进行处理

        while (1)

        {

            pthread_mutex_lock(&work_mutex);

            if(work_area[0]!='\0')//功能线程函数还没有处理完毕

            {

                pthread_mutex_unlock(&work_mutex);

                sleep(1);

            }

            else

                break;   //处理完毕退出

        }

    }

    pthread_mutex_unlock(&work_mutex);

    

    printf("Waiting for thread to finish...\n");

    res=pthread_join(a_thread, &thread_result);//等待功能线程退出

    if(res!=0)

    {

        printf("Thread join failed\n");

        exit(EXIT_FAILURE);

    }

    printf("Thread joined\n");

    pthread_mutex_destroy(&work_mutex);//销毁互斥量

    return 0;

}

 

3.一个子父进程的简单对话,利用消息队列传递信息,用信号量实现实现交替发送消息,首先子进程发送消息,父进程接收到消息,然后回消息,然后子进程再回消息,交替进行,输入“exit”退出对话。

这个是自己瞎折腾想的,写的并不怎么好,子父进程对话似乎没有什么问题,但是在退出的时候就会有问题,如果子进程发送“exit”,会出现p操作失败,提示信号量标识被删除的提示(Identifier removed),父进程如果先发送"exit"会出现Interrupted system call,在终端还有可能无法退出来,后来查资料也没有得出个所以然,后来我在后面添加了wait,等待子程序退出后,程序一切都变得正常了,难道是父进程先退出,然后子进程成孤儿了,然后就在后台默默的执行,但是我明明让它退出来的呀,还得多磨练磨练。。。

 

信号量的控制:

两个信号量  sem1=0,sem2=1;

子进程:

p(sem2)

读取消息,显示,发送消息

v(sem1)

 

父进程:

p(sem1)

读取消息,显示,发送消息

v(sem2)

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>

#include <pthread.h>

#include <time.h>

#include <sys/ipc.h>

#include <sys/sem.h>

#include <semaphore.h>

#include <sys/msg.h>

#include <errno.h>

#define BUF_SIZE 1024

//定义消息队列结构

struct msgbuf

{

    long mtype;          //消息类型

    char mtext[BUF_SIZE]; //消息缓冲区

    

};

/*

union semun

{

    int val;

    struct semid_ds *buf;

    unsigned short *array;

};

*/

//初始化信号量

int init_sem(int sem_id,int id,int val)

{

    union semun sem_union;

    sem_union.val=val;      //信号量的值

    if(semctl(sem_id, id, SETVAL,sem_union)==-1)

    {

        perror("init fail...");

        return -1;

    }

    return 0;

}

// p操作函数

int sem_p(int sem_id,int id)

{

    struct sembuf sem_b;

    sem_b.sem_num=id;   //信号量编号

    sem_b.sem_op=-1;  //信号量操作,取值为-1表示p操作

    sem_b.sem_flg=SEM_UNDO; //在进程没有释放信号量而退出时,系统自动释放进程中未释放的信号量。

    if(semop(sem_id, &sem_b, 1)==-1)  //进行p操作

    {

    

        perror("p fail...");

        return -1;

    }

    return 0;

}

// v操作函数

int sem_v(int sem_id,int id)

{

    struct sembuf sem_b;

    sem_b.sem_num=id;   //信号量编号

    sem_b.sem_op=1;  //信号量操作,取值为+1表示v操作

    sem_b.sem_flg=SEM_UNDO; //在进程没有释放信号量而退出时,系统自动释放进程中未释放的信号量。

    if(semop(sem_id, &sem_b, 1)==-1)  //进行p操作

    {

    

        perror("p fail...");

        return -1;

    }

    return 0;

}

//删除信号量

int del_sem(int sem_id,int id)

{

    if(semctl(sem_id, id, IPC_RMID,0)==-1)

    {

        perror("delete fail...");

        return -1;

    }

    return 0;

}

int main()

{

    pid_t pid;

    int qid;    //消息队列标识

    int sem_id;//信号量标识

    key_t key;  //键值

    struct msgbuf buf; //消息缓冲区

    

    key=ftok("/home", 'y');    //生成信号量的键值

    sem_id=semget(key,  2, IPC_CREAT|0666);  //创建一个信号量集

    if (sem_id<0)

    {

        perror("semget error");

        exit(1);

    }

    key=ftok("/home", 'm');    //生成消息队列的键值

    qid=msgget(key,  IPC_CREAT|0666);//创建一个消息队列

    if(qid<0)

    {

        perror("msgget error");

        exit(1);

    }

    init_sem(sem_id,  0,0);  //初始化信号量0

    init_sem(sem_id,  1,1);  //初始化信号量1

    

    pid=fork();

    if(pid<0)

    {

        printf("fail...\n");

        exit(EXIT_FAILURE);

    }

    else if(pid==0) //子进程

    {

        

        while (1)

        {

            sem_p(sem_id,1);   //p操作

            buf.mtext[0]='\0';

            msgrcv(qid, &buf,BUF_SIZE, 0, IPC_NOWAIT); //读取消息队列中的一个的消息

            if (strncmp(buf.mtext, "exit",4)==0)  //如果键盘输入exit,退出循环

            {

                strcpy(buf.mtext, "exit");

                buf.mtype=getpid();

                msgsnd(qid, &buf,BUF_SIZE , IPC_NOWAIT);  //向消息队列中发送一个消息

                sem_v(sem_id,0);  //v操作

                break;

            }

            if(buf.mtext[0]!='\0')

            {

                printf("父进程:%s\n",buf.mtext);

            }

            printf("子进程说:");

            fgets(buf.mtext,BUF_SIZE,stdin);  //从键盘输入消息的内容

            buf.mtext[strlen(buf.mtext)-1]='\0';

            buf.mtype=getpid();

            msgsnd(qid, &buf,BUF_SIZE , IPC_NOWAIT);  //向消息队列中发送一个消息

            sem_v(sem_id,0);  //v操作

        }

        exit(EXIT_SUCCESS);

    }

    else if(pid>0)  //父进程

    {

        while (1)

        {

            sem_p(sem_id,0);

            buf.mtext[0]='\0';

            msgrcv(qid, &buf,BUF_SIZE, 0, IPC_NOWAIT); //读取消息队列中的一个的消息

            if (strncmp(buf.mtext, "exit",4)==0)  //如果键盘输入exit,退出循环

            {

                strcpy(buf.mtext, "exit");

                buf.mtype=getpid();

                msgsnd(qid, &buf,BUF_SIZE , IPC_NOWAIT);  //向消息队列中发送一个消息

                sem_v(sem_id,1);

                break;

            }

            if(buf.mtext[0]!='\0')

            {

                printf("子进程:%s\n",buf.mtext);

            }

            printf("父进程说:");

            fgets(buf.mtext,BUF_SIZE,stdin);  //从键盘输入消息的内容

            buf.mtext[strlen(buf.mtext)-1]='\0';

            buf.mtype=getpid();

            msgsnd(qid, &buf,BUF_SIZE , IPC_NOWAIT);  //向消息队列中发送一个消息

            sem_v(sem_id,1);

        }

        wait(NULL);

        del_sem(sem_id,1); //删除信号量

        msgctl(qid,  IPC_RMID, NULL);  //删除消息队列

    }

    

    return 0;

}

 

 

 

 

待续。。。

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值