线程的属性
文章目录
- 线程的属性
- 一、线程的属性----分离属性
- 二、线程的取消
- 1.一般主线程不用于去处理任务,只是控制子线程状态,例如取消,接合.. -> pthread_cancel()
- 2.设置线程响应取消的状态-> pthread_setcancelstate()
- 3.设置线程响应取消的类型-> pthread_setcanceltype()
- 三、线程取消例程函数
- 1.线程取消例程函数
- 2.使用取消例程函数
- 3.实现
- 四、多条线程同时访问某一片内存空间导致数据践踏
- 线程同步互斥方式
- 1.同步互斥
- 2.、处理同步互斥方式
- 六、同步互斥方式 -- 有名信号量
- 1.有名信号量的特点
- 2.有名信号量的函数接口
- 3.有名信号量与信号量的比较
- 同步互斥方式 --- 无名信号量
- 无名信号量
- 2.无名信号量 函数接口
一、线程的属性----分离属性
1.分离属性
首先分离属性是线程的一个属性,有了分离属性的线程,不需要别的线程去接合自己的(回收自己的资源)。
但是虽然说是分离的,但是进程退出了,该线程还是会退出的。
设置了分离属性的线程 -> 不需要pthread_join()
设置了非分离属性的线程 -> 需要pthread_join() -> 默认创建的普通属性线程就是非分离线程
2.创建分离属性的线程
pthread_attr_setdetachstate()
方法一:添加一个分离属性到一个属性变量中,然后使用属性变量去创建一个线程,那么创建出来的线程就是具有分离属性的线程
1)定义一个属性变量 -> 数据类型:pthread_attr_t
2)初始化属性变量 -> pthread_attr_init()
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
参数:
attr:未初始化的属性变量
返回值:
成功:0
失败:非0错误码
3)设置分离属性到属性变量中//set(设置)detach(分离)state(状态)
#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,.....); -> 创建出来的线程就是分离属性的线程,不需要pthread_join()
5)销毁属性变量 -> pthread_attr_destroy()
int pthread_attr_destroy(pthread_attr_t *attr);
参数:
attr:已经初始化过的属性变量
返回值:
成功:0
失败:非0错误码
代码说明:
#include<stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
//方法1 : 如何创建具有分离属性的线程
void* start_routine(void *arg)
{
printf("[%lu]start_routine\n",pthread_self()); //pthread_self打印当前线程的ID号
sleep(3);
}
int main()
{
//1、定义一个线程属性变量
pthread_attr_t attr;
//2、初始化清空属性变量
pthread_attr_init(&attr);
//3、把分离属性加入到属性变量中
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
//4、创建一条具有分离属性的子线程
pthread_t thread;
pthread_create(&thread,&attr,start_routine,NULL);
//5、销毁属性变量
pthread_attr_destroy(&attr);
pause();//暂停自己
}
方法二:先创建一个普通线程,然后在线程中调用一个设置分离属性的函数,那么这个线程就变成分离的属性
1)设置线程本身的属性为分离属性 -> pthread_detach()
#include <pthread.h>
int pthread_detach(pthread_t thread);
函数作用:
设置分离属性给线程
参数:
thread:需要设置分离属性的线程的ID号
返回值:
成功:0
失败:非0错误码
2)获取线程的ID号-> pthread_self()
#include <pthread.h>
pthread_t pthread_self(void);
参数:
无
返回值:线程的ID号。
#include<stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
void* start_routine(void *arg)
{
//在子线程的内部 设置 分离属性
pthread_detach(pthread_self());
printf("[%lu]start_routine\n",pthread_self()); //pthread_self打印当前线程的ID号
}
int main()
{
//4、创建一条具有分离属性的子线程
pthread_t thread;
pthread_create(&thread,NULL,start_routine,NULL);
//pause();
}
代码说明:
#include<stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
//方法2:如何创建具有分离属性的线程
void* start_routine(void *arg)
{
//在子线程的内部设置分离属性
pthread_detach(pthread_self());
printf("[%lu]start_routine\n",pthread_self()); //pthread_self打印当前线程的ID号
}
int main()
{
//4、创建一条具有分离属性的子线程
pthread_t thread;
int cnt=0;
while(1)
{
int ret= pthread_create(&thread,NULL,start_routine,NULL);
if(ret != 0){
printf("pthread_create error\n");
break;
}
printf("cnt:%d\n",cnt++);
}
}
总结:
无论是否添加了分离属性的线程,理论上创建的线程的数量没有限制。
用两种方法设置分离属性的子线程,主线程都无法结合成功
二、线程的取消
1.一般主线程不用于去处理任务,只是控制子线程状态,例如取消,接合… -> pthread_cancel()
主线程 -> 取消请求 -> 子线程
#include <pthread.h>
int pthread_cancel(pthread_t thread);
函数作用:
发送一个取消请求给子线程。
参数:
thread:需要取消的线程的ID号。
返回值:
成功:0
失败:非0错误码
注意:线程收到取消请求,就等价于提前退出。
pthread_exit() -> 是线程主动退出 ->退出值-> pthread_join()
主线程给子线程发送取消请求--->子线程收到取消请求-> 线程被迫退出(被动退出) -> 没有退出值
例子:使用pthread_cancel去取消一个线程
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
int pthread_status = 10;
//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{
int cnt=30;
while(cnt--)
{
sleep(1);
printf("[%lu]start_routine cnt:%d\n",pthread_self(),cnt); //pthread_self 打印自己的线程ID号
}
pthread_exit(&pthread_status);
}
int main()
{
//创建一条子线程
pthread_t thread;
pthread_create(&thread,NULL,start_routine,NULL);
sleep(5);
//给子线程发送一个取消请求
pthread_cancel(thread);
//等待子线程退出
void *p = NULL;
pthread_join(thread,&p); // p = (void*)&pthread_status
//如果此处对p进行解引用就会产生段错误
return 0;
现象说明:
取消线程之后,主线程是无法接受它的返回值
p没有被函数pthread_join分配空间,它还是NULL指针,对空指针进行解引用就段错误
如果子线程没有分离属性,主线程是可以结合成功,但是没有返回值
2.设置线程响应取消的状态-> pthread_setcancelstate()
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
参数:
state:
PTHREAD_CANCEL_ENABLE -> 能响应 -> 线程默认属性
-> 是马上响应,还是延迟响应 -> 取决于type。
PTHREAD_CANCEL_DISABLE -> 不能响应
oldstate:保留之前的状态,如果不关心,则填NULL。
返回值:
成功:0
失败:非0错误码。
说明:
pthread_setcancelstate一般使用在子线程中
If a thread has disabled cancellation, then a cancellation request remains queued until the thread enables cancellation.
//如果一个线程不能响应取消的,那么在这个过程中收到了取消请求,那么这个请求会直到这个线程能响应取消请求为止才会被响应。(主线程发送的取消命令没有被丢弃,只是挂起)
If a cancellation request is received, it is blocked until cancelability is enabled.
//如果收到取消请求,那么就会阻塞到这个线程能响应为止
代码说明:
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
int pthread_status = 20;
//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{
//子线程设置不响应取消请求
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
int cnt=0;
while(1)
{
sleep(1);
printf("start_routine:%lu cnt:%d\n",pthread_self(),cnt++); //pthread_self 打印自己的线程ID号
if(cnt == 10)
{
//子线程设置 取消请求能响应
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , NULL);
}
}
}
int main()
{
//创建一条子线程
pthread_t thread;
pthread_create(&thread,NULL,start_routine,NULL);
sleep(2);
//给子线程发送一个取消请求
pthread_cancel(thread);
//等待子线程退出
pthread_join(thread,NULL);
return 0;
现象说明:
刚开始子线程不能接收取消请求,10秒之后才能取消
3.设置线程响应取消的类型-> pthread_setcanceltype()
#include <pthread.h>
int pthread_setcanceltype(int type, int *oldtype);
参数:
type:
PTHREAD_CANCEL_DEFERRED -> 延迟取消 -> 遇到一个取消点函数才会响应取消-> 线程默认属性
PTHREAD_CANCEL_ASYNCHRONOUS -> 立即响应
oldtype:保存之前的状态,如果不关心,则填NULL。
注意: 线程是遇到取消点函数之后才会响应取消的
取消点函数有哪些呢? -> man 7 pthreads
fprintf()
fputc()
fputs()
sleep()
printf()
usleep()
例子1:线程延迟取消,遇到取消点才会取消
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{
while(1)
{
//printf("start_routine\n");//取消点函数
//sleep(1); //取消点函数
}
}
int main()
{
//创建一条子线程
pthread_t thread;
pthread_create(&thread,NULL,start_routine,NULL);
//给子线程发送一个取消请求
pthread_cancel(thread);
//等待子线程退出
pthread_join(thread,NULL);
return 0;
}
现象说明:主线程给子线程 发送线程取消 ,但是子线程在执行过程中没有遇到取消点函数,所以不能响应取消请求
例子2:给子线程设置立即响应取消请求,收到主线程发来的取消请求,立即响应
#include<stdio.h>
#include<pthread.h>
//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{
//当线程收到取消请求之后,立即响应,不需要遇到取消点
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
while(1)
{
//printf("start_routine\n");//取消点函数
//sleep(1); //取消点函数
}
}
int main()
{
//创建一条子线程
pthread_t thread;
pthread_create(&thread,NULL,start_routine,NULL);
//给子线程发送一个取消请求
pthread_cancel(thread);
//等待子线程退出
pthread_join(thread,NULL);
return 0;
}
现象说明:子线程收到主线程的取消请求后,立即响应退出线程
三、线程取消例程函数
1.线程取消例程函数
当线程收到取消请求时,先不要马上响应取消请求,而是执行一个例程函数先,执行完这个函数再响应取消。(类似于进程里面的signal–>函数句柄)
2.使用取消例程函数
为了防止线程带着一些公共资源而被取消掉,如果带着资源来退出,那么其他线程无法再次使用该资源
3.实现
1)压栈线程的取消例程函数-> pthread_cleanup_push()
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),void *arg);
参数:
routine: 线程的取消例程函数 -> 必须是: void fun(void *arg)
arg:传递给取消例程函数的参数
2)弹栈线程的取消例程函数 --> pthread_cleanup_pop()
#include <pthread.h>
void pthread_cleanup_pop(int execute);
参数:
execute:
0 -> 删除时,直接删除。
非0 -> 删除时,会先执行一遍例程函数,再删除。
模型:
void* fun(void *arg)
{
pthread_cleanup_push(xxxx); -> 将来收到取消请求,就会执行xxxx这个函数。
…;
…; <-- 收到取消请求
…;
pthread_cleanup_pop(0);
}
注意(取消例程函数的三要素):
1.子线程收到取消请求之后,(遇到取消点函数)就会执行线程取消例程函数,然后执行完就响应取消请求直接退出,不会再往下面执行了 。
2.如果子线程没有收到取消请求,而且程序执行到pthread_cleanup_pop该函数时,此函数才会执行,
并且根据参数决定是否执行线程取消例程函数再退出子线程。
3.这两个函数都必须是成对出现的,如果只写一个直接编译会报错
代码说明:
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
void routine(void *arg)
{
printf("线程取消例程\n");
}
//线程例程,创建一条线程之后,去执行这个函数
void* start_routine(void *arg)
{
//压栈
pthread_cleanup_push(routine,NULL);
while(1)
{
printf("start_routine\n");
sleep(1);
}
//弹栈
pthread_cleanup_pop(0);//0不执行 非0才执行
}
int main()
{
int ret=0;
//创建一条子线程
pthread_t thread;
pthread_create(&thread,NULL,start_routine,NULL);
if(ret != 0)
{
perror("pthread_create fail");
return -1;
}
sleep(5);
//给子线程发送一个取消请求
pthread_cancel(thread);
//等待子线程退出
pthread_join(thread,NULL);
return 0;
}
现象说明:子线程收到取消请求之后,立即执行取消例程函数。pop后面的函数不执行
四、多条线程同时访问某一片内存空间导致数据践踏
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
//全局变量-->共享变量
int g_val = 0;
//线程例程,创建一条线程之后,去执行这个函数
void* start_routine1(void *arg)
{
g_val = 100;
sleep(2);//延时2秒,此时线程2执行g_val = 200; 该变量的值已经被线程2改变了
printf("start_routine1 g_val:%d\n",g_val);
pthread_exit(NULL);
}
//线程例程,创建一条线程之后,去执行这个函数
void* start_routine2(void *arg)
{
sleep(1);//延时1秒,此时 线程1 执行 g_val = 100;
g_val = 200;
printf("start_routine2 g_val:%d\n",g_val);
pthread_exit(NULL);
}
int main()
{
//创建一条子线程
pthread_t thread1;
pthread_create(&thread1,NULL,start_routine1,NULL);
//创建一条子线程
pthread_t thread2;
pthread_create(&thread2,NULL,start_routine2,NULL);
//等待子线程退出
pthread_join(thread1,NULL);
return 0;
}
现象说明:
start_routine2 g_val:200
start_routine1 g_val:200
结论:线程1和线程2同时访问某一片内存空间,导致数据发生践踏。
如何去避免呢?? 也就是线程1在访问这片内存空间时,别的线程不能进来。—使用同步互斥机制
(各个线程操作数据的时候没有保护机制,很容易自己的数据被其它的线程篡改,很危险!!!)
线程同步互斥方式
1.同步互斥
同步互斥就是使得线程处理任务时有先后的顺序,为了防止线程资源(共享资源)被抢占的问题
2.、处理同步互斥方式
信号量 -> 进程 (共享内存+信号量一起使用) —进程IPC中的一种
有名信号量 -> 进程 (共享内存+有名信号量一起使用) <编译的时候需要链接线程库-lpthread>
无名信号量 -> 线程
互斥锁 -> 线程
读写锁 -> 线程
条件变量 -> 线程(互斥锁+条件变量一起使用)
六、同步互斥方式 – 有名信号量
1.有名信号量的特点
有名信号量与信号量非常相似,但是信号量的值只能是0/1,而是有名信号量可以0~正无穷。
信号量使用空间 + 数据来处理互斥,而有名信号量只是使用数据来处理。(一般是作用于进程之间)
2.有名信号量的函数接口
1)创建并打开一个有名信号量 -> sem_open()
#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:八进制权限,例如: 0777
value:有名信号量的起始值
注意:
如果oflag中有O_CREAT这个选项,则这个mode与value必须要填。
如果有名信号量已经存在的了,但是你又写了O_CREAT,那么后面你填的mode和value就会被忽略。
返回值:
成功:有名信号量的地址
失败:SEM_FAILED -> NULL / (sem_t *)-1
2)有名信号量的P操作
P操作: 资源数-1操作 -> sem_wait() -> man 3 sem_wait
#include <semaphore.h>
int sem_wait(sem_t *sem); -> 如果减不了1,这个函数就会阻塞
参数:
sem:有名信号量的地址
返回值:
成功:0 -> 资源数可以-1
失败:-1
比如:
如果当前的值为2,那么sem_wait()就会马上返回,并且将值设置为1。
如果当前的值为1,那么sem_wait()就会马上返回,并且将值设置为0。
如果当前的值为0,那么sem_wait()就会阻塞,一直阻塞到有名信号量的值不为0为止。
3)有名信号量的V操作
V操作: 资源数+1操作 -> sem_post() -> man 3 sem_post
#include <semaphore.h>
int sem_post(sem_t *sem); -> 一定可以+1的,绝对不会阻塞
参数:
sem:有名信号量的地址
返回值:
成功:0 -> 资源数可以+1
失败:-1
4)关闭有名信号量。 -> sem_close()
#include <semaphore.h>
int sem_close(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功:0
失败:-1
5)删除有名信号量。 -> sem_unlink()
#include <semaphore.h>
int sem_unlink(const char *name);
参数:
name: 有名信号量的名字
返回值:
成功:0
失败:-1
3.有名信号量与信号量的比较
不同点:
1)信号量函数复杂,有名信号量函数简单。
2)信号量有空间和数据两个需要操作;有名信号量只有数据一个需要操作。
3)信号量编译的时候不需要线程库;有名信号量编译的时候需要链接线程库。
相同点:
1)都是作用于进程之间的通信
2)都有P/V操作;P是减1操作,V是加1操作
代码说明:
进程1:
#include<stdio.h>
#include<string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#define SEM_NAME "/semname1"
int main()
{
//1、获取key值
key_t key = ftok(".",10);
//2、根据key值 获取共享内存的ID号
int shmid = shmget(key,1024,IPC_CREAT|0666);
//3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域
char*shm_p = shmat(shmid,NULL,0);
if(shm_p == (void*)-1)
{
perror("shmat error");
return -1;
}
//4、创建并打开一个有名信号量
sem_t *nameSem = sem_open(SEM_NAME,O_CREAT,0777,0);
if(nameSem == SEM_FAILED )
{
printf("sem_open error\n");
return -1;
}
//此时映射出来的shm_p 就是两个进程的共享内存
while(1)
{
//从键盘上获取数据,存储到共享内存shm_p
scanf("%s",shm_p);
//有名信号量的V操作 数据 +1
sem_post(nameSem);
//退出条件,这里要注意 应该使用strncmp 指定字节数
if(strncmp(shm_p,"exit",4) == 0)
break;
}
//4、当不再使用时,解除映射关系
shmdt(shm_p);
//5、当没有进程再需要使用这块共享内存时,删除它
shmctl(shmid, IPC_RMID, NULL);
//6、关闭有名信号量。
sem_close(nameSem);
//7、删除有名信号量
sem_unlink(SEM_NAME);
return 0;
}
进程2:
#include<stdio.h>
#include<string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#define SEM_NAME "/semname1"
int main()
{
//1、获取key值
key_t key = ftok(".",10);
//2、根据key值 获取共享内存的ID号
int shmid = shmget(key,1024,IPC_CREAT|0666);
//3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域
char*shm_p = shmat(shmid,NULL,0);
if(shm_p == (void*)-1)
{
perror("shmat error");
return -1;
}
//4、创建并打开一个有名信号量
sem_t *nameSem = sem_open(SEM_NAME,O_CREAT,0777,0);
if(nameSem == SEM_FAILED )
{
printf("sem_open error\n");
return -1;
}
//此时映射出来的shm_p 就是两个进程的共享内存
while(1)
{
//有名信号量的P操作 数据 -1
sem_wait(nameSem);
//从共享内存中读取数据
printf("recv:%s\n",shm_p);
//退出条件,这里要注意 应该使用strncmp 指定字节数
if(strncmp(shm_p,"exit",4) == 0)
break;
}
return 0;
同步互斥方式 — 无名信号量
无名信号量
一般作用于线程之间的互斥,由于是无名信号量,所以说是没有名字的,不能使用sem_open打开
2.无名信号量 函数接口
1)定义一个无名信号量 (数据类型: sem_t)
sem_t sem; -> 无名信号量不是一个文件,而是一个变量
2)初始化无名信号量。 -> sem_init()
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem: 无名信号量的变量的地址
pshared:
0 -> 作用于线程之间 -> 只考虑这种情况
非0-> 作用于进程之间
value: 无名信号量的起始值。
返回值:
成功:0
失败:-1
3)无名信号量的P操作
P操作: 资源数-1操作 -> sem_wait() -> man 3 sem_wait
#include <semaphore.h>
int sem_wait(sem_t *sem); -> 如果减不了1,这个函数就会阻塞
参数:
sem:无名信号量的地址
返回值:
成功:0 -> 资源数可以-1
失败:-1
比如:
如果当前的值为2,那么sem_wait()就会马上返回,并且将值设置为1。
如果当前的值为1,那么sem_wait()就会马上返回,并且将值设置为0。
如果当前的值为0,那么sem_wait()就会阻塞,一直阻塞到有名信号量的值不为0为止
4)无名信号量的V操作
V操作: 资源数+1操作 -> sem_post() -> man 3 sem_post
#include <semaphore.h>
int sem_post(sem_t *sem); -> 一定可以+1的,绝对不会阻塞
参数:
sem:无名信号量的地址
返回值:
成功:0 -> 资源数可以+1
失败:-1
5)销毁无名信号量。 -> sem_destroy()
#include <semaphore.h>
int sem_destroy(sem_t *sem);
参数:
sem:无名信号量的地址
返回值:
成功:0
失败:-1
例子:线程使用无名信号量实现互斥
#include<stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <semaphore.h>
//1、定义一个无名信号量变量
sem_t g_sem;
int g_val = 0;
//线程1的例程函数
void* start_routine1(void *arg)
{
printf("%s start ...\n",__FUNCTION__);
//在访问 共享资源之前,先看看有没有其他线程正在使用
//无名信号量的P操作
//也就是说,能不能进行 -1 操作 如果能 则往下面 走
//如果不能 ,则阻塞在这里
sem_wait(&g_sem);
g_val = 200;
sleep(2);
printf("start_routine1 g_val:%d\n",g_val);
//当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
sem_post(&g_sem);
printf("%s end ...\n",__FUNCTION__);
}
//线程2的例程函数
void* start_routine2(void *arg)
{
printf("%s start ...\n",__FUNCTION__);
sem_wait(&g_sem);
sleep(1);
g_val = 400;
printf("start_routine2 g_val:%d\n",g_val);
//当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
sem_post(&g_sem);
printf("%s end ...\n",__FUNCTION__);
}
//线程3 的 例程函数
void* start_routine3(void *arg)
{
printf("%s start ...\n",__FUNCTION__);
sem_wait(&g_sem);
sleep(1);
g_val = 600;
printf("start_routine3 g_val:%d\n",g_val);
//当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
sem_post(&g_sem);
printf("%s end ...\n",__FUNCTION__);
}
int main()
{
//2、初始化无名信号量 ---sem_init
sem_init(&g_sem,0, 1);
//创建一条子线程
pthread_t thread1;
pthread_create(&thread1,NULL,start_routine1,NULL);
pthread_t thread2;
pthread_create(&thread2,NULL,start_routine2,NULL);
pthread_t thread3;
pthread_create(&thread3,NULL,start_routine3,NULL);
sem_wait(&g_sem);
g_val = 1000;
printf("main g_val:%d\n",g_val);
//当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
sem_post(&g_sem);
//阻塞等待子线程退出,回收资源
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
pthread_join(thread3,NULL);
//销毁无名信号量
sem_destroy(&g_sem);
}
练习6: 有一个进程,创建5个线程出来,每一个线程任务都是一样。
任务: 将"helloworld"字符串每隔1S打印一个字符。 -> 任务:10S
结果: hhhhheeeeellllllllllooooowwwwwooooorrrrrlllllddddd
1S 1S 1S 1S 1S 1S 1S 1S 1S 1S
代码参考:
#include<stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
//线程1 的 例程函数
void* start_routine(void *arg)
{
int cnt = 0;
char str[] = "helloworld";
while(1)
{
sleep(1);
fprintf(stderr,"%c",str[cnt++]); //stde
rr 标准出错 ,没有缓冲区
if(cnt == strlen(str))
break;
}
pthread_exit(NULL);
}
int main()
{
//创建5条子线程
pthread_t thread[5];
for(int i=0; i<5; i++){
pthread_create(&(thread[i]),NULL,start_routine,NULL);
}
//阻塞等待子线程退出,回收资源
for(int i=0; i<5; i++){
pthread_join(thread[i],NULL);
}
return 0;
}
练习7: 有一个进程,创建5个线程出来,每一个线程任务都是一样。 (使用无名信号量去处理同步互斥) -> 0是不能变成-1的
任务: 将"helloworld"字符串每隔1S打印一个字符。 -> 任务:10S
结果: helloworldhelloworldhelloworldhelloworldhelloworld
10S 10S 10S 10S 10S
代码参考:
#include<stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
//1、定义一个无名信号量
sem_t g_sem;
void* start_routine(void*arg)
{
//进行P 操作 -1
sem_wait(&g_sem);
char str[ ] = "helloworld";
for(int i=0;i<strlen(str);i++)
{
sleep(1);
fprintf(stderr,"%c",str[i]);
}
//进行V操作 + 1
sem_post(&g_sem);
pthread_exit(0);
}
int main()
{
//2、初始化无名信号量
sem_init(&g_sem,0,1);
//创建5条子线程
pthread_t thread[5];
for(int i=0; i<5; i++){
pthread_create(&(thread[i]),NULL,start_routine,NULL);
}
//阻塞等待子线程退出,回收资源
for(int i=0; i<5; i++){
pthread_join(thread[i],NULL);
}
sem_destroy(&g_sem);
return 0;
}