1、线程的概念:
线程是比进程更小的活动单元,他是进程中一个执行路径
线程同进程共用进程的地址空间。
特点:
1)创建一个线程比创建一个进程开销小了很多。
2)实现线程之间的通讯是十分方便,因为这些线程都是共享资源的。
3)线程是一个动态的概念。是一个执行的分支,这个分支就是执行一个函数的调用,并发运行,如果这个函数执行完了,这个线程也结束。
4)主线程可以创建子线程。子线程的运行情况不会影响主线程,主线程会影响子线程。主线程结束了退出,子线程也会退出。
5)进程是操作系统分配资源的最小的单位,线程是调度的最小单位。
2.线程的创建步骤
1)创建线程、
#include <pthread.h>
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
thread:参数类型pthread_t 用来保存线程的id
attr:描述的是线程的属性,如果说不需要修改线程的属性,可以设置为NULL。
void *(*start_routine) (void *): 这个表示的是一个函数指针,就是说我们这个线程执行起来之后,要做的事情,就在这函数里面做。
void *arg:函数的参数是一个void *,如果要传参的话,把参数放在这个地方。
返回值:成功返回0, 失败返回-1
2)退出线程
1.void pthread_exit(void *retval);
只有一个参数,这个参数就是返回的结果,这个返回的类型可以是任意的,看你的需要来返回。
返回的结果,会给到在同一个进程里面,调用这个函数的线程:pthread_join,它就可以接收对应的结果。
2.int pthread_cancel(pthread_t thread);
我们可以在主线程里面,发送一个取消信号,来取消指定的线程。thread这个参数就是我们所需要取消的线程的id。
在子线程里面,我们可以拒绝取消的请求。也可以使能取消的请求。
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
3.函数的正常退出,结束。
3)接收线程退出之后的结果
int pthread_join(pthread_t thread, void **retval);
thread:这个是我们要接收返回值的线程的ID。
**retval:这个是返回的结果。
3、线程中信号量的使用(POSIX)
1)无名信号量:用于线程间的同步和互斥。
1.声明并且初始化一个信号量:
声明一个有名信号量:sem_t sem;
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem:指我们所需要初始化的这个信号量。
pshared :一般都是用0,表示这个信号量在一个进程里面的线程之间共享。
value:就是我们要初始化信号量的值是多少。
返回值:成功返回0,失败返回-1;
3.初始化好了之后,就可以直接调用对应的函数进行P/V操作
int sem_wait(sem_t *sem); 这个就是P操作,这个是阻塞型的,会一直等待有可用的资源。
int sem_trywait(sem_t *sem); 这个也是P操作,但是这个不阻塞,如果没有可用的资源就立刻返回。
int sem_post(sem_t *sem); 这个是V操作
返回值: 成功返回0,失败返回-1;
3.用完之后,销毁它。
int sem_destroy(sem_t *sem);
返回值:成功返回0,失败返回-1;
2)有名信号量:用于进程间的同步和互斥。
1.声明并且初始化一个信号量。
声明一个有名信号量:sem_t *sem;
#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:信号量的名字 “name”
oflag:如何打开,可以是创建的方式打开,O_CREAT | O_RDWR | O_EXCL,
如果使用这两种打开方式O_CREAT | O_EXCL,打开一个已经存在的信号量
将会返回错误,就说明这个信号量已经存在了,我们直接打开就可以了。
mode:0666
value:这个代表信号量的初始值。
返回值:如果成功返回的是信号量的地址。
如果失败,有一种特殊情况,EEXIST ( 需要包含#include <errno.h> ),这种情况说明这个信号量已经存在了,我们直接调用第一个打开函数去打开就可以了。
(2)初始化好了之后,就可以直接调用对应的函数进行P/V操作
#include <semaphore.h>
int sem_wait(sem_t *sem); 这个就是P操作,这个是阻塞型的,会一直等待有可用的资源。
int sem_trywait(sem_t *sem); 这个也是P操作,但是这个不阻塞,如果没有可用的资源就立刻返回。
int sem_post(sem_t *sem); 这个是V操作
返回值:成功返回0,失败返回-1;
(3)用完之后,销毁它。
int sem_close(sem_t *sem);
返回值:成功返回0, 失败返回-1;
(4)如果想删除在文件系统里面的这个信号量节点,可以使用下面的函数:
int sem_unlink(const char *name); //这个参数和上面的name是一样的。
今日代码:
/*************************************************************************
> File Name: skread.c
> Author: csgec
> Mail: [email protected]
> Created Time: Thu 26 Jul 2018 10:20:28 AM HKT
************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<semaphore.h>
#include<errno.h>
int main()
{
int shmid;//共享内存ID
char *p = NULL;
pid_t key;
sem_t *data,*space;//声明有名信号量
//有名信号量的创建
data = sem_open("sem1",O_CREAT,0666,0);
if((int)data < 0)
{
printf("read 已经存在创建好了的信号量1\n");
data = sem_open("sem1",O_RDWR);
}
//有名信号量的创建
space = sem_open("sem2",O_CREAT,0666,1);
if((int)space < 0)
{
printf("read 已经存在创建好了的信号量2\n");
space = sem_open("sem2",O_RDWR);
}
//创建or获得这个IPC对象,这个IPC对象就是共享内存,返回的是这个共享内存的id
key = ftok(".",22);
if(key < 0)
{
perror("ftok fail");
exit(-1);
}
#if 0
//创建2个信号量,不同进程就可以根据这个信号量的ID去找到这个信号量
semid = semget(key,2,IPC_CREAT | IPC_EXCL | 0666);
if(semid < 0)
{
printf("ipc2创建信号量错误\n");
semid = semget(key,2,0666);
}
else
{
// printf("正在初始化.......\n");
init_sem(semid,data,0);
init_sem(semid,space,1);
}
#endif
//申请内存共享,返回共享内存的ID值
shmid = shmget(key,4096,IPC_CREAT | 0666);
if(shmid < 0)
{
perror("shmget fail");
exit(-1);
}
p = shmat(shmid,NULL,0);//将共享内存映射到进程空间
// memset(p,0,sizeof(char *));
if((int)p == -1)
{
perror("映射内存失败");
exit(-1);
}
int i=0;
while(1)
{
i++;
sem_wait(data);
fprintf(stderr,"%c ",*p);
if(i == 10)
{
printf("\n");
i = 0;
}
sem_post(space);
}
sem_close(data);
sem_close(space);
sem_unlink("sem1");
sem_unlink("sem2");
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////
/*************************************************************************
> File Name: skwrite.c
> Author: csgec
> Mail: [email protected]
> Created Time: Thu 26 Jul 2018 10:20:28 AM HKT
************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<semaphore.h>
#include<errno.h>
int main()
{
int shmid;//共享内存ID
char *p;
pid_t key;
sem_t *data,*space;//声明有名信号量
//有名型号量的创建
data = sem_open("sem1",O_CREAT,0666,0);
if((int)data < 0)
{
printf("write 已经存在创建好了的信号量1\n");
data = sem_open("sem1",O_RDWR);
}
//有名型号量的创建
space = sem_open("sem2",O_CREAT,0666,1);
if((int)space < 0)
{
printf("write 已经存在创建好了的信号量2\n");
space = sem_open("sem2",O_RDWR);
}
//创建or获得这个IPC对象,这个IPC对象就是共享内存,返回的是这个共享内存的id
key = ftok(".",22);
if(key < 0)
{
perror("ftok fail");
exit(-1);
}
//申请内存共享,返回共享内存的ID值
shmid = shmget(key,4096,IPC_CREAT | 0666);
if(shmid < 0)
{
perror("shmget fail");
exit(-1);
}
p = shmat(shmid,NULL,0);//将共享内存映射到进程空间
if((int)p == -1)
{
perror("映射内存失败");
exit(-1);
}
char *msg = "0123456789";
int i = 0;
while(1)
{
sem_wait(space);
memcpy(p,msg+i,1);
sleep(1);
i = (i+1) % 10;
sem_post(data);
}
sem_close(data);
sem_close(space);
sem_unlink("sem1");
sem_unlink("sem2");
return 0;
}