系统编程学习日记四--共享内存、信号量、线程、线程的分离属性、线程的取消、有名信号量

本文详细介绍了进程间通信的共享内存机制,包括如何申请、使用和管理共享内存,以及如何结合信号量解决并发问题。此外,还探讨了线程的概念,线程的创建、接合、退出以及分离属性。重点讲解了线程的取消和同步互斥,如信号量的使用、有名信号量的操作函数以及线程的取消例程。文章通过实例代码展示了如何在C语言中实现这些功能。
摘要由CSDN通过智能技术生成

一、进程之间的通信方式-----共享内存。

1.共享内存属于IPC对象。所以在使用共享内存之前也要申请key值与ID号

2.共享内存的作用机制以及范围 ?

 

作用机制:任意一个进程可以将数据放置到共享内存上,另外一个进程就可以将共享内存上的数据输出

作用范围:由于共享内存任意一个进程都可以看到,所以作用范围是linux下的任意两个进程。

 

二、关于共享内存的函数接口

1.申请key值。

   key_t key=ftok(“.”,10);

2.申请共享内存的ID号 --shmget  -------man 2 shmget

功能:

shmget - allocates a System V shared memory segment    

   //允许进程申请共享内存的空间

使用方法:

       #include <sys/ipc.h>
       #include <sys/shm.h>                                       

       int shmget(key_t key, size_t size, int shmflg);

参数:

       key:  key值

      size :共享内存的总字节数们必须是PAGE_SIZE的倍数

        shmflg:  IPC_CREATE|0666   ---不存在则创建 

返回值:

   成功: 共享内存的ID号

   失败: -1;

 

3.根据你的ID号去申请共享内存的区域   ------shmat()----man 2 shmat

使用方法
       #include <sys/types.h>
       #include <sys/shm.h>

       void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

       shmid:共享内存的ID号

       shmaddr:NULL 选择共享内存的地址,NULL代表系统来分配 

       shmflg:   设置为0   普通属性即可

返回值:

   成功:共享内存的地址p

   失败 : NULL

 

4.直接通过IO操作将数据放置到空间上

  fgets(p, 1024,stdin);//从键盘获取数据

5.获取共享内存的数据

 print(“%s\n”,p);

6.撤销映射  -----shmdt   -------man 2 shmdt

使用方法

       #include <sys/types.h>
       #include <sys/shm.h>

     int shmdt(const void *shmaddr);

参数:

      shmaddr    :共享内存的地址

返回值

   成功 0;

  失败 -1

7.删除共享内存   -----shmctl -----man  2 shmctl

使用方法

       #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数

shmid: 共享内存的ID号

cmd: IPC_RMID  ---删除共享内存

buf:  如果是删除填NULL

 

返回值:

 成功0 

失败-1

 

三、信号量

共享内存一般很少自己单独使用,一般都是与信号量一起使用的。

1.什么是信号量

共享内存单独使用会出现数据践踏的情况,很明显是因为没有处理好互斥问题 ,如果信号量与共享内存连同,那么就可以解决共享内存的问题。有了信号量,就可以事项共享内存输入一次 ,输出一次。

2.关于信号量的函数接口

1).先申请key值

 key_t key = ftok(".",10);

2)根据key值去申请信号量的ID号 ------semget ------ man 2 semget

函数功能:

       semget - get a System V semaphore set identifier

//获取信号量的ID。

使用方法:
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semget(key_t key, int nsems, int semflg);

参数:、

key ; key值

nsems  信号量中元素的个数     2

semflg:  IPC_CREATE|0666   不存在就创建

返回值:

    成功:信号量的ID号

    失败 -1

3)如何初始化和删除信号量     ----  semctl ------ man 2 semctl

 

使用方法:

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semctl(int semid, int semnum, int cmd, ...);

参数:

 semid;信号量的ID号

semnum:控制空间->0   控制数据->1

cmd: :IPC_RMID      ----------删除信号量

               SETVAL           ----------初始化信号量的值

例如:

semcrl(semid,0,SETVAL,1);

semcrl(semid,1,SETVAL,0);

返回值 

        成功: 0

        失败:-1

4)如何实现信号量的P/V操作 ---------semop     ---------man 2 semop

P操作;1变0;

V操作: 0变1

使用方法:

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semop(int semid, struct sembuf *sops, size_t nsops);

参数 :

 semid:信号量的ID

sops:

struct sembuf{

           unsigned   short   sem_num;  /*需要操作的成员下标*/    比如操作空间---0   操作数据 ----1
           short          sem_op;   /* p操作/v操作  */        比如p操作---  -1  v操作----   1
           short          sem_flg;  /* 普通属性   0*/

}

nsops:信号量操作结构体的个数   ----1

返回值: 

         成功: 0

         失败:-1

共享内存实现通讯

 client.c

#include "head.h"

int main(int argc,char *argv[])
{
	//1. 申请key值。
	key_t key = ftok(".",10);
	key_t keyx = ftok(".",20);

	//2. 根据key值去申请共享内存和信号量的ID号。
	int shmid = shmget(key,1024,IPC_CREAT|0666);
	int semid = semget(keyx,2,IPC_CREAT|0666);

	//3. 根据id号去产生一块映射的空间。
	char *p = shmat(shmid,NULL,0);

	//4. 设置信号量的初始值。
	semctl(semid,0,SETVAL,1); //有车位
	semctl(semid,1,SETVAL,0); //没车

	//空间p操作
	struct sembuf space;
	space.sem_num = 0; //空间
	space.sem_op = -1; //p操作
	space.sem_flg = 0; //普通属性

	struct sembuf data;
	data.sem_num = 1;  //数据
	data.sem_op = 1;   //v操作
	data.sem_flg = 0;  //普通属性

	//5. 不断阻塞地从键盘获取数据,并存放到共享内存中。
	while(1)
	{
		//现在情况: 空间:1  数据:0
		//请问空间能不能减1?
		semop(semid,&space,1);

		//现在情况: 空间:0  数据:0
		//开车进去
		fgets(p,1024,stdin);

		//数据v操作
		semop(semid,&data,1);
		//现在情况: 空间:0  数据:1

		if(strncmp(p,"quit",4) == 0)
		{
			break;
		}
	}

	return 0;
}

sever.c

#include "head.h"

int main(int argc,char *argv[])
{
		//1. 申请key值。
	key_t key = ftok(".",10);
	key_t keyx = ftok(".",20);

	//2. 根据key值去申请共享内存和信号量的ID号。
	int shmid = shmget(key,1024,IPC_CREAT|0666);
	int semid = semget(keyx,2,IPC_CREAT|0666);

	//3. 根据id号去产生一块映射的空间。
	char *p = shmat(shmid,NULL,0);

	//4. 设置信号量的初始值。
	semctl(semid,0,SETVAL,1); //有车位
	semctl(semid,1,SETVAL,0); //没车

	//空间v操作
	struct sembuf space;
	space.sem_num = 0; //空间
	space.sem_op = 1; //v操作
	space.sem_flg = 0; //普通属性

	//数据p操作
	struct sembuf data;
	data.sem_num = 1;   //数据
	data.sem_op = -1;   //p操作
	data.sem_flg = 0;   //普通属性

	//4. 不断将共享内存的数据打印出来。
	while(1)
	{
		//现在情况: 空间:0  数据:1
		//请问数据能不能减1?
		semop(semid,&data,1);

		//现在情况: 空间:0  数据:0
		//开车出去
		printf("from shm:%s",p);

		//空间v操作。
		semop(semid,&space,1);

		if(strncmp(p,"quit",4) == 0)
		{
			break;
		}
	}

	//5. 撤销映射。
	shmdt(p);

	//6. 删除共享内存。
	shmctl(shmid,IPC_RMID,NULL);

	//7. 删除信号量。
	semctl(semid,0,IPC_RMID);

	return 0;
}

四、线程概念

1.进程与线程有什么区别:

线程是进程的内部资源

 

2.线程函数的特点

1)查看线程库中的函数接口  man 3 xxx

2)线程函数头文件 #include<pthread.h>

五、学习函数接口

1.如何创建一个子线程  --------pthread_create()----man  3 pthread_create()

使用方法:

       #include <pthread.h>

       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

参数:

thread:  存储线程ID号的变量的地址        变量的数据类型:pthread_t

attr:  线程的属性,普通属性设置为NULL

start_routine: 线程执行的例程函数,就是开启一个线程后,这个线程需要做什么东西。函数名长这样void *fun (void *arg)

arg:传递给例程函数的参数

返回值:

        成功  0;

        失败  非0;

#include "head.h"

void *fun(void *arg)
{
    int a = *(int *)arg;

    for (int i = 0; i < a; i++)
    {
        sleep(1);
        printf("xiaole\n");
    }
}

int main()
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    pthread_t tid;

    int a = 10;
    pthread_create(&tid, &attr, fun, (void *)&a);
    sleep(2);
    int ret;
    ret = pthread_join(tid, NULL);
    if (ret == 0)
    {
        printf("succ\n");
    }
    else
    {
        printf("!succ\n");
    }

    pthread_attr_destroy(&attr);
    return 0;
}

注意:

 Compile and link with -pthread.

//只要代码涉及到线程库,编译时就必须要链接线程库。

eg:gcc test.c -o test -lpthread

#include "head.h"

void *fun(void *a)
{
    for (int i = 0; i < 5; i++)
    {
        sleep(1);
        printf("321\n");
    }
}

int main()
{

    /*单进程*/
    pthread_t tid;
    pthread_create(&tid, NULL, fun, NULL);

    int i;
    for (i = 0; i < 10; i++)
    {
        sleep(1);
        printf("123\n");
    }

    return 0;
}

主线程 一旦结束,所有的子线程不论是否结束都会结束

2.如何接合一个线程(等待一个子进程的退出)  -----》pthread_join()-----man  3 pthread_join

函数功能:

     //接合一个结束的进程

使用方法:

       #include <pthread.h>

       int pthread_join(pthread_t thread, void **retval);

参数:

thread   需要接合的哪个线程的ID号

retval   存储的子线程的退出值的指针。如果设置为NULL,代表不关心子线程的退出状态

 

返回值:

 成功: 0

 失败:非0

#include "head.h"
int exit_state = 10;

void *fun(void *a)
{

    for (int i = 0; i < 10; i++)
    {
        sleep(1);
        printf("321\n");
    }
    pthread_exit((void *)(&exit_state));
}

int main()
{

    /*单进程*/
    pthread_t tid;
    pthread_create(&tid, NULL, fun, NULL);

    int i;
    for (i = 0; i < 5; i++)
    {
        sleep(1);
        printf("123\n");
    }
    void *p = NULL;
    pthread_join(tid, &p);
    printf("%d\n", *(int *)p);

    return 0;
}

 

3.如何退出一个线程,并将退出值返回给主线程  ————pthread_exit()  -------man 3 pthread_exit

//函数功能:

       pthread_exit - terminate calling thread

//结束一个正在运行的线程

使用方法:
       #include <pthread.h>

       void pthread_exit(void *retval);

参数:

       retval  :线程的退出值变量的地址

返回值

      无

#include "head.h"
int exit_state=10;

void *fun(void *a)
{
    
    for (int i = 0; i < 10; i++)
    {
        sleep(1);
        printf("321\n");
    }
    pthread_exit((void*)(&exit_state));
}

int main()
{

    /*单进程*/
    pthread_t tid;
    pthread_create(&tid, NULL, fun, NULL);
    

    int i;
    for (i = 0; i < 5; i++)
    {
        sleep(1);
        printf("123\n");
    }
    void*p=NULL;
    pthread_join(tid,&p);
    printf("%d\n",*(int *)p);
    

    return 0;
}

4.导致线程退出的几种情况

①当线程调用pthread_exit()这个函数是,可以导致线程退出,并且可以携带一个退出值给这个进程中另外调用pthread_join()函数的线程

②当线程历程函数返回时,也可以导致线程的退出。这种情况等价于调用                int sig=10;      

pthread_exit((void*)&sid) 等价于  return ((void*)&sid)

③如果进程被取消了,那么线程也会推出

④在进程中任意一个线程调用exit(),或者是主线程从main函数中返回。都代表进程退出

通过线程实现消息队列通信

jack.c

#include "head.h"

#define J2R 10
#define R2J 20

struct msgbuf{
	long mtype;
	char mtext[50];
};

void *snd_fun(void *arg)  //arg = (void *)&msgid
{
	int msgid = *(int *)arg;

	struct msgbuf gec;
	int ret;
	while(1)
	{
		bzero(&gec,sizeof(gec));

		gec.mtype = J2R;
		fgets(gec.mtext,sizeof(gec.mtext),stdin);

		ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);
		if(ret == -1)
		{
			printf("msgsnd error!\n");
			exit(-1);
		}

		if(strncmp(gec.mtext,"quit",4) == 0)
		{
			exit(0);  //进程退出,可以导致所有的线程都退出。
		}
	}
}

void *rcv_fun(void *arg)
{
	int msgid = *(int *)arg;

	struct msgbuf gec;
	int ret;
	while(1)
	{
		bzero(&gec,sizeof(gec));

		ret = msgrcv(msgid,&gec,sizeof(gec.mtext),R2J,0);
		if(ret == -1)
		{
			printf("msgrcv error!\n");
			exit(-1);
		}
		printf("from Rose:%s",gec.mtext);

		if(strncmp(gec.mtext,"quit",4) == 0)
		{
			msgctl(msgid,IPC_RMID,NULL);
			exit(0);
		}
	}
}

int main(int argc,char *argv[])
{
	//1. 申请消息队列的key值与ID号。
	key_t key = ftok(".",10);
	int msgid = msgget(key,IPC_CREAT|0666);

	//2. 创建两个子线程。
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,snd_fun,(void *)&msgid);
	pthread_create(&tid2,NULL,rcv_fun,(void *)&msgid);

	//3. 接合线程。
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	return 0;
}

rose.c

#include "head.h"

#define J2R 10
#define R2J 20

struct msgbuf{
	long mtype;
	char mtext[50];
};

void *snd_fun(void *arg)  //arg = (void *)&msgid
{
	int msgid = *(int *)arg;

	struct msgbuf gec;
	int ret;
	while(1)
	{
		bzero(&gec,sizeof(gec));

		gec.mtype = R2J;
		fgets(gec.mtext,sizeof(gec.mtext),stdin);

		ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);
		if(ret == -1)
		{
			printf("msgsnd error!\n");
			exit(-1);
		}

		if(strncmp(gec.mtext,"quit",4) == 0)
		{
			exit(0);  //进程退出,可以导致所有的线程都退出。
		}
	}
}

void *rcv_fun(void *arg)
{
	int msgid = *(int *)arg;

	struct msgbuf gec;
	int ret;
	while(1)
	{
		bzero(&gec,sizeof(gec));

		ret = msgrcv(msgid,&gec,sizeof(gec.mtext),J2R,0);
		if(ret == -1)
		{
			printf("msgrcv error!\n");
			exit(-1);
		}
		printf("from Jack:%s",gec.mtext);

		if(strncmp(gec.mtext,"quit",4) == 0)
		{
			msgctl(msgid,IPC_RMID,NULL);
			exit(0);
		}
	}
}

int main(int argc,char *argv[])
{
	//1. 申请消息队列的key值与ID号。
	key_t key = ftok(".",10);
	int msgid = msgget(key,IPC_CREAT|0666);

	//2. 创建两个子线程。
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,snd_fun,(void *)&msgid);
	pthread_create(&tid2,NULL,rcv_fun,(void *)&msgid);

	//3. 接合线程。
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	return 0;
}

六、线程的分离属性

1.什么是分离属性:

       分离属性是线程的一个属性,有了分离属性的线程,是不需要别的线程来接合自己的。

       虽然说是分离的,但是进程退出时,该线程还是要退出

 

       分离属性线程  -------不需要调用pthread_join去接合的

       非分离属性线程  -------需要调用pthread_join去接合的  ----默认创建

2.如何创建分离属性的进程?

方法一:添加一个分离属性到一个属性变量中,在使用这个属性变量去创建一个线程,创建出来的线程就具有分离属性

1)定义一个属性变量 ---------》数据类型  pthread_attr_t

         pthread_attr_t   attr;

2)初始化属性变量 -  -------pthread_attr_init   --- man 3 pthread_attr_init

  功能:

      initialize thread attributes object

      //初始化线程属性变量

使用方法:

       #include <pthread.h>

       int pthread_attr_init(pthread_attr_t *attr);

  参数:

      attr:属性变量的地址 

  返回值:

        成功0;

        失败非0

3)添加分离属性到书信变量中 ------- pthread_attr_setdetachstate----man 3 pthread_attr_setdetachstate

功能:

      set detach state attribute in thread attributes object

      //添加分离属性到书信变量中

使用方法:

       #include <pthread.h>

       int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

参数:

       attr:属性变量的地址 

     detachstate:

          PTHREAD_CREATE_DETACHED    -----分离属性

          PTHREAD_CREATE_JOINABLE -----------非分离属性

返回值:

        成功 0 

        失败 非0

4)利用这个属性变量去创建一个线程

      pthread_create(&tid,&attr,fun,NULL);       --> 这样创建出来的线程就是分离属性的线程,不需要别人去join它的。

5)销毁属性变量 -------pthread_attr_destroy--------man 3 pthread_attr_destroy

使用方法:

       #include <pthread.h>
       int pthread_attr_destroy(pthread_attr_t *attr);

参数:

  attr:属性变量的地址

返回值

成功 0 

失败  非0

 

代码 查看分离属性的线程是否会被接合

#include "head.h"

void *fun(void *arg)
{
    int a = *(int *)arg;

    for (int i = 0; i < a; i++)
    {
        sleep(1);
        printf("xiaole\n");
    }
}

int main()
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    pthread_t tid;

    int a = 10;
    pthread_create(&tid, &attr, fun, (void *)&a);
    sleep(2);
    int ret;
    ret = pthread_join(tid, NULL);
    if (ret == 0)
    {
        printf("succ\n");
    }
    else
    {
        printf("!succ\n");
    }

    pthread_attr_destroy(&attr);
    return 0;
}

方法二: 先创建一个普通的线程、然后再普通的线程中去调用设置分离属性的函数,呢这个线程就变成分离属性的线程了。

1)设置线程本身为分离属性-------pthread_deatch--------man 3 pthread_detach

功能:     

 pthread_detach - detach a thread

        //分离一个线程

使用方法:
       #include <pthread.h>

       int pthread_detach(pthread_t thread);
参数

     thread: 线程的ID号

返回值

成功 0 

失败  非0

2)再线程内部获取自己的ID号  pthread_self   ---------man 3 pthread_self   

功能:

pthread_self - obtain ID of the calling thread
//获取线程的ID

使用方法:

       #include <pthread.h>

       pthread_t pthread_self(void);

参数 :无

返回值:

    成功: 线程的ID

    失败:不会失败
 

#include "head.h"

void *fun(void *arg)
{
    pthread_t tid = pthread_self();
    pthread_detach(tid);
    int a = *(int *)arg;

    for (int i = 0; i < a; i++)
    {
        sleep(1);
        printf("xiaole\n");
    }
}

int main()
{

    pthread_t tid;

    int a = 10;
    pthread_create(&tid, NULL, fun, (void *)&a);
    sleep(2);
    int ret;
    ret = pthread_join(tid, NULL);
    if (ret == 0)
    {
        printf("succ\n");
    }
    else
    {
        printf("!succ\n");
    }

    return 0;
}

 

七、线程的取消

1、什么是线程的取消?

通过发送一个取消请求给线程,那么这个线程收到请求之后,就会提前退出。

2、如何发送? -> pthread_cancel() -> man 3 pthread_cancel

函数功能:

      send a cancellation request to a thread

使用格式:

      #include <pthread.h>

   int pthread_cancel(pthread_t thread);

参数:

       thread: 你想取消的那个线程的ID号。

返回值:

成功:0

失败:非0

 

3.设置线程取消相应的状态     ---------pthread_setcancelstate   -------man 3 pthread_setcancelstate

功能:

       pthread_setcancelstate, pthread_setcanceltype - set cancelability state
       and type

使用方法:
       #include <pthread.h>

       int pthread_setcancelstate(int state, int *oldstate);
 

参数:

      state

              PTHREAD_CANCEL_ENABLE     ====能响应

              PTHREAD_CANCEL_DISABLE  ---------不能响应

oldstate  :保留之前状态的指针,如果不想保留则设置为NULL

 

返回值

      成功 0 

      失败  非0

注意:

 If  a  cancellation  request  is received, it is blocked until cancelability is enabled.

//如果再不能响应取消的的情况下,收到一个取消请求,那么就会一直阻塞到这个线程能响应取消为止、

#include "head.h"

void *fun(void *arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    for (int i = 0; i < 10; i++)
    {
        sleep(1);
        printf("xiao le\n");
    }
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, fun, NULL);
    sleep(5);
    pthread_cancel(tid);
    return 0;
}

 

4.设置线程的取消类型 ----pthread_setcancel  ------man 3 pthread_setcanceltype

功能:
       pthread_setcancelstate, pthread_setcanceltype - set cancelability state
       and type

使用方法:
       #include <pthread.h>
       int pthread_setcanceltype(int type, int *oldtype);

 

参数:保留之前状态的指针,如果不想保留则设置为NULL

返回值

      成功 0 

      失败  非0

type:

          PTHREAD_CANCEL_DEFERRED         ------延迟响应   (默认)
 

          PTHREAD_CANCEL_ASYNCHRONOUS -----------立即响应

oldtype:

          

 

 

1)当线程下一行代码是取消点函数时,就会响应

2)取消点函数有哪些   ------  man 7 pthreads

#include "head.h"

void *fun(void *arg)
{
	int i,j;
	for(i=0;i<100000;i++)
	{
		for(j=0;j<100000;j++)
		{
			//在此期间,都没有遇到取消点函数。
		}
	}

	while(1)
	{
		fputc('a',stderr);       //取消点  //输出一个字符到屏幕上。
		printf("helloworld!\n"); //取消点
	}

}

int main(int argc,char *argv[])
{
	//1. 创建线程。
	pthread_t tid;
	pthread_create(&tid,NULL,fun,NULL);

	//2. 马上发送取消请求。
	pthread_cancel(tid);
	printf("我已经发送了取消请求给你了!\n");

	//3. 接合。
	pthread_join(tid,NULL);

	return 0;
}

 

八.线程的取消例程函数

1.什么是线程的取消例程函数?

当线程收到取消请求时,先不要马上响应取消,而是先执行一个例程函数,执完这个例程函数之后再推出。

2.为什么要使用线程取消例程函数?

因为在一个进程中,有可能由多个线程同时使用一个个资源,如果某一个线程在使用资源时,因为被取消而提前退出,那么这个线程就会带着这个资源而退出,导致其他线程无法继续使用这个线程、

 

解决方案: 先告诉线程,将来在收到请求时,先释放资源,再响应取消,那么这么做,就可以避免这些线程带着资源退出。

3.如何实现?
1)如何在线程历程中说明线程的取消例程函数是哪个  ----pthread_cleanup_push  --------man 3 pthread_cleanup_push

使用方法
       #include <pthread.h>

       void pthread_cleanup_push(void (*routine)(void *),void *arg);

参数:

routine: 线程的取消例程函数。必须长 void fun(void *arg)

arg : 传递给线程的取消例程函数的参数

返回值:无

 

信号处理函数:   void fun (int sig)

线程例程函数:   void *fun(void *arg)

线程取消例程函数: void fun(void *arg)

2)删除 取消例程函数。------》pthread_cleanup_pop ----------man 3 pthread_cleanup_pop

使用方法:
       #include <pthread.h>
       void pthread_cleanup_pop(int execute);

参数:

       execute  :非0 ---------》在删除时,先执行一遍线程的取消例程函数,再删除、

                          0   ---------在删除时,会马上删除

返回值 : 无

#include "head.h"

void myfun(void*arg){
    printf("hellow\n");
}

void *fun(void *arg)
{
    pthread_cleanup_push(myfun,NULL);
	int i,j;
	for(i=0;i<5;i++)
	{
        sleep(1);
		printf("helloworld!\n"); //取消点
	}
	pthread_cleanup_pop(1);
}

int main(int argc,char *argv[])
{
	//1. 创建线程。
	pthread_t tid;
	pthread_create(&tid,NULL,fun,NULL);

	//2. 马上发送取消请求
    sleep(2);
	pthread_cancel(tid);
	printf("我已经发送了取消请求给你了!\n");

	//3. 接合。
	pthread_join(tid,NULL);

	return 0;
}

注意

1.取消历程函数是会被压倒栈中,需要删除

2.pthread_cleanup_pop和pthread_cleanup_push是配合一起使用的。

 

九、同步互斥方式   ---------有名信号量

1.在进程与线程中,同步互斥的方式有哪些

1)信号量   ------------处理进程之间的互斥

2)有名信号量------------处理进程之间的互斥

3)无名信号量------------处理线程之间的互斥

4)互斥锁------------处理线程之间的互斥

5)读写锁------------处理线程之间的互斥

 

2.什么是同步互斥?为什么要进行同步互斥?

同步互斥就是为了线程/进程处理任务时有先后顺序,为了防止线程/进程抢占资源。

3.有名信号量的函数接口?

1)创建并打开一个有名信号量,   -------sem_open -------man 3 sem_open

函数功能:
       sem_open - initialize and open a named semaphore

//初始化并打开一个有名信号量

使用方法:
       #include <fcntl.h>           /* For O_* constants */
       #include <sys/stat.h>        /* For mode constants */
       #include <semaphore.h>

       sem_t *sem_open(const char *name, int oflag);              打开一个存在的有名信号量
       sem_t *sem_open(const char *name, int oflag,  mode_t mode, unsigned int value);  创建并打开一个不存在的有名信号量

参数:

   name   :要求必须以”/“作为开头     例如“/sem_test”   ---------最终会在/dev/shm目录下创建

oflag:O_CREAT   ----不存在就会创建,存在就会忽略

           O_CREAT | O_EXCL   -------不存在就会创建,存在就会出错

mode :  八进制权限  eg:0777

value : 有名信号量的起始值

返回值:

         成功:有名信号量的地址  sem_t *

         失败:NULL

注意:

如果在oflag中有O_CREATE这个选项,那么剩余两个参数mode和value必须要填

如果有名信号量已经存在了,但是你有写了O_CREATE,那么后面的两个参数就算你填了也会忽略。

//虽然这个函数不是以pthread开头,但是编译时,也要链接线程库

3)如何关闭有名信号量  ---------sem_close  -----man 3 sem_close

功能:
       sem_close - close a named semaphore

//关闭有名信号量 

使用方法:
       #include <semaphore.h>

       int sem_close(sem_t *sem);

       Link with -pthread.

参数:

sem:有名信号量的地址

返回值

     成功  0 

      失败-1

关闭信号的意思是:只是关闭而已。下次使用直接打开,不用去创建

 

3)如何删除有名信号量  -------sem_unlink  ---------man 3 sem_unlink 

功能
       sem_unlink - remove a named semaphore

/./删除有名信号量 

使用方法:
       #include <semaphore.h>

       int sem_unlink(const char *name);

       Link with -pthread.

参数:
     name:有名信号量的名字

返回值

    成功  0 

    失败-1

4)有名信号量的p操作   sem_wait   -------man 3 sem_wait   

功能:

       sem_wait, sem_timedwait, sem_trywait - lock a semaphore

使用方法:
       #include <semaphore.h>

       int sem_wait(sem_t *sem);

参数:

sem:有名信号量的地址

返回值: 

     成功: 0

     失败-1

如果当前值为2 ,那么调用sem_wait就会马上返回,并且将值设置为1

如果当前值为1 ,那么调用sem_wait就会马上返回,并且将值设置为0

如果当前值为0 ,那么调用sem_wait就会阻塞,要等到值变为1为止,才可以减一

 

5)又名信号量的V操作   -------- sem_post ---------man 3 sem_post

       sem_post - unlock a semaphore

使用方法:
       #include <semaphore.h>

       int sem_post(sem_t *sem);
 

参数:

sem:有名信号量的地址

返回值: 

     成功: 0

     失败-1

如果当前值为0,那么调用sem_post就会马上返回,并且将值设置为1

如果当前值为1 ,那么调用sem_post就会马上返回,并且将值设置为2

如果当前值为2 ,那么调用sem_post就会马上返回,并且将值设置为3

…………

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值