线程基本操作
创建线程
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<pthread.h>
#include<signal.h>
void * thread_jobs(void * arg)
{
//普通线程任务区
printf("thread tid %x\n",(unsigned int)pthread_self());
while(1)
sleep(1);
}
int main(void)
{
//主线程任务区
pthread_t tid;
int err;
if((err = pthread_create(&tid,NULL,thread_jobs,NULL))>0){
printf(" failed : %s\n",strerror(err));
exit(0); // err > 0 线程创建失败 退出进程
}
printf("主线程的pid:%x\n被创建出来的线程的pid:%x\n",(unsigned int)pthread_self(),(unsigned int)tid);
//防止主线程过早退出,看不到普通线程效果
while(1)
sleep(1);
return 0;
}
main函数里面执行的代码是主线程,jobs里是普通线程
create传出的tid,与普通线程里面的tid相同,但不是一个,只有pthread_self()里面出来的tid才代表该线程还活着
join阻塞回收
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<pthread.h>
#include<signal.h>
void * thread_jobs(void * arg)
{
//普通线程任务区
int flag = 5;
while(flag--){
printf("thread tid %x\n",(unsigned int)pthread_self());
sleep(1);
}
pthread_exit((void*)6);
}
int main(void)
{
//主线程任务区
pthread_t tid;
int err;
if((err = pthread_create(&tid,NULL,thread_jobs,NULL))>0){
printf(" failed : %s\n",strerror(err));
exit(0); //退出进程
}
printf("主线程的pid:%x\n被创建出来的线程的pid:%x\n",(unsigned int)pthread_self(),(unsigned int)tid);
//等待线程执行
void * reval = NULL;
if((err=pthread_join(tid,&reval))==0){
printf("Master thread join success , thread reval %ld\n",(long int)reval);
}
while(1)
sleep(1);
return 0;
}
等待线程执行五秒后再回收。
cancel杀死线程
不等普通线程运行完,三秒之后直接杀死普通线程,返回值-1
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<pthread.h>
#include<signal.h>
void * thread_jobs(void * arg)
{
//普通线程任务区
int flag = 5;
while(flag--){
printf("thread tid %x\n",(unsigned int)pthread_self());
sleep(1);
}
pthread_exit((void*)6);
}
int main(void)
{
//主线程任务区
pthread_t tid;
int err;
if((err = pthread_create(&tid,NULL,thread_jobs,NULL))>0){
printf(" failed : %s\n",strerror(err));
exit(0); //退出进程
}
printf("主线程的pid:%x\n被创建出来的线程的pid:%x\n",(unsigned int)pthread_self(),(unsigned int)tid);
//等待线程执行
void * reval = NULL;
sleep(3);
pthread_cancel(tid);
if((err=pthread_join(tid,&reval))==0){
printf("Master thread join success , thread reval %ld\n",(long int)reval);
}
while(1)
sleep(1);
return 0;
}
detach设置线程分离态
这段代码不知道为什么始终都走不到回收失败那段。。。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<pthread.h>
#include<signal.h>
void * thread_jobs(void * arg)
{
//普通线程任务区
int flag = 5;
pthread_detach(pthread_self());
while(1){
}
pthread_exit((void*)6);
}
int main(void)
{
//主线程任务区
pthread_t tid;
int err;
if((err = pthread_create(&tid,NULL,thread_jobs,NULL))>0){
printf(" failed : %s\n",strerror(err));
exit(0); //退出进程
}
printf("主线程的pid:%x\n被创建出来的线程的pid:%x\n",(unsigned int)pthread_self(),(unsigned int)tid);
//等待线程执行
void * reval = NULL;
if((err=pthread_join(tid,&reval))==0){
printf("Master thread join success , thread reval %ld\n",(long int)reval);
}else{
printf("join failed : %s\n",strerror(err));
}
while(1)
sleep(1);
return 0;
}
~
定义线程属性并修改
pthread_create的第二个参数就是线程属性,传入NULL代表默认系统给出的线程属性
线程属性是一个结构体,用法为 pthread_attr_t 变量名
线程属性结构体中的成员分别有:线程的警戒缓冲区、线程的优先级指针、线程的退出状态、线程栈地址、线程栈大小
步骤:
1.定义线程属性
2.初始属性(默认)
3.修改属性
4.创建线程并使用自定义属性 pthread(&tid,&attr,jobs,NULL)
用到的函数:
查看默认属性下,线程的退出状态:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <fcntl.h>
int main()
{
pthread_attr_t attr;
//1.初始化线程属性
pthread_attr_init(&attr);
//2.检测线程属性中的线程退出状态
int detach_status;
pthread_attr_getdetachstate(&attr , &detach_status);
//3.判断是回收态还是分离态
if(detach_status == PTHREAD_CREATE_JOINABLE)
{
printf("线程属性默认为回收态\n");
}
else
{
printf("线程属性默认为分离态\n");
}
//4.释放线程属性结构体内存
pthread_attr_destroy(&attr);
printf("进程退出!\n");
}
线程互斥
互斥锁
pthread_mutex_t lock ; //定义一个互斥锁的结构体
pthread_mutex_init(&lock , &attr); 实现互斥锁的初始化
pthread_mutex_destroy(&lock); 释放锁资源所占用的内存
pthread_mutex_lock(&lock); 上锁
pthread_mutex_unlock(&lock); 解锁
两个线程各对全局变量num++ 一共加5000次,每次加1 确保加的时候只有单一线程在操作不会造成线程冲突对数据错误修改。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <pthread.h>
#include <fcntl.h>
//将次数定义成宏
#define CONT 5000
//将互斥锁与num定义成全局变量,来让两个线程都可以获取该锁和num
pthread_mutex_t lock;
int num = 0;
//两个线程的工作是一样的,所以这里只用定义一个工作函数
void* thread_job(void* arg)
{
int i = 0;
pthread_detach(pthread_self());//将线程设置为分离态,让系统自己回收
//对num进行循环+1操作
//注意,不要把锁加在循环外面,如果放在外面,就代表着让一个线程一次性加满5000次后再让另一个线程加
//如果把锁家在循环外面,两个线程的工作效率还不如单线程工作效率高
while(i < CONT)
{
pthread_mutex_lock(&lock);//上锁
//这就是临界区代码,在上锁与解锁之间的代码就是临界区代码
num++;
i++;
printf("thread No.0x%x ++num , num = %d\n" , (unsigned int)pthread_self() , num);
pthread_mutex_unlock(&lock);//解锁
}
pthread_exit(NULL);
}
int main()
{
//初始化互斥锁
pthread_mutex_init(&lock , NULL);
pthread_t tids[2];
//创造线程A和线程B
pthread_create(&tids[0] , NULL , thread_job , NULL);
pthread_create(&tids[1] , NULL , thread_job , NULL);
//让主线程循环睡眠,来让线程A和线程B获取时间片
//由于我们把线程设置成了分离态,系统会自动回收线程,不用我们操心
while(1)
{
sleep(1);
}
//回收锁资源
pthread_mutex_destroy(&lock);
exit(0);
}
读写锁
读锁可以多个线程同时读,写锁只允许一个线程写
读的时候不能写,写的时候不能读
定义num,创建八个线程,三个线程负责用写锁,五个线程用读锁
int number; //全集变量num=0
pthread_rwlock_t lock; //定义读写锁
void * write_thread(void * arg) //写线程
{
while(1){
pthread_rwlock_wrlock(&lock); //申请写锁
printf("writr 0x%x , ++number %d\n",(unsigned int)pthread_self(),++number); //对num进行++操作,并打印出操作该线程的tid
pthread_rwlock_unlock(&lock); //解锁
usleep(100000);
}
}
void * read_thread(void *arg)
{
while(1){
pthread_rwlock_rdlock(&lock);
printf("read 0x%x , number %d\n",(unsigned int)pthread_self(),number);
pthread_rwlock_unlock(&lock);
usleep(100000);
}
}
int main(void)
{
int i=0;
pthread_t tids[8];
pthread_rwlock_init(&lock,NULL); //初始化读写锁
for(i;i<3;i++)
pthread_create(&tids[i],NULL,write_thread,NULL); //三个线程写锁
for(i;i<8;i++)
pthread_create(&tids[i],NULL,read_thread,NULL); //后五个线程读锁
while(i--){
pthread_join(tids[i],NULL); //线程全部回收
}
pthread_rwlock_destroy(&lock); //释放锁资源
return 0;
}
进程互斥锁
通过修改互斥锁的属性,将线程互斥锁变为进程互斥锁,让多进程使用
pthread_mutex_init(&lock , &attr); 实现互斥锁的初始化 ,具体改变的就是互斥锁初始化的第二个参数。
例:
实现父子进程,分别对number++ 5000次
共享一个结构体,打包了共享数据number , 与pthread mutex_t进程互斥锁
没上锁的情况:
#include<sys/mman.h>
#include<sys/wait.h>
typedef struct
{
int number;
pthread_mutex_t lock; //父子进程共享number,和一把锁
}shared_t;
int main(void)
{
int fd;
fd = open("MAP_FILE",O_RDWR);
ftruncate(fd,sizeof(shared_t)); //共享文件开辟空间,大小为一个结构体
//互斥锁属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);
//映射
shared_t * ptr = NULL;
ptr = mmap(NULL,sizeof(shared_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //共享成功,返回映射的首地址
//初始化数据
ptr->number = 0;
pthread_mutex_init(&ptr->lock,&attr); //自定义锁属性初始化锁
//父子进程
pid_t pid;
pid = fork();
if(pid>0){
for(int i=0;i<5000;i++){
printf("parent %d ++number %d\n",getpid(),++(ptr->number));
}
wait(NULL); //防止僵尸进程,回收子进程
}else if(pid == 0){
for(int i=0;i<5000;i++){
printf("child %d ++number %d\n",getpid(),++(ptr->number));
}
exit(0); //子进程先执行完退出,父回收
}else{
perror("fork failed");
}
printf("process done\n");
return 0;
}
由于进程冲突,结果不稳定
上锁的情况:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<pthread.h>
#include<signal.h>
#include<sys/mman.h>
#include<sys/wait.h>
typedef struct
{
int number;
pthread_mutex_t lock; //父子进程共享number,和一把锁
}shared_t;
int main(void)
{
int fd;
fd = open("MAP_FILE",O_RDWR);
ftruncate(fd,sizeof(shared_t)); //共享文件开辟空间,大小为一个结构体
//互斥锁属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);
//映射
shared_t * ptr = NULL;
ptr = mmap(NULL,sizeof(shared_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //共享成功,返回映射的首地址
//初始化数据
ptr->number = 0;
pthread_mutex_init(&ptr->lock,&attr); //自定义锁属性初始化锁
//父子进程
pid_t pid;
pid = fork();
if(pid>0){
for(int i=0;i<5000;i++){
pthread_mutex_lock(&ptr->lock); //上锁
printf("parent %d ++number %d\n",getpid(),++(ptr->number));
pthread_mutex_unlock(&ptr->lock); //解锁
}
wait(NULL); //防止僵尸进程,回收子进程
}else if(pid == 0){
for(int i=0;i<5000;i++){
pthread_mutex_lock(&ptr->lock);
printf("child %d ++number %d\n",getpid(),++(ptr->number));
pthread_mutex_unlock(&ptr->lock);
}
exit(0); //子进程先执行完退出,父回收
}else{
perror("fork failed");
}
//释放资源
pthread_mutexattr_destroy(&attr); //释放锁属性
pthread_mutex_destroy(&ptr->lock); //释放锁资源
munmap(ptr,sizeof(shared_t)); //释放映射,释放顺序不能变,锁空间在映射空间内,应该先释放锁,再释放映射空间,否则会出现段错误
printf("process done\n");
return 0;
}
情况稳定,不管运行多少次都是10000
文件锁
文件结构体里面有 文件锁属性结构体,其中包含了各种属性,如果想对文件锁属性进行修改,可以写一个新的结构体对旧的结构体进行替换从而修改文件锁属性
两个进程对一个文件,进行上锁,解锁,又上锁,又解锁的操作。。。
线程同步
nclude<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<pthread.h>
#include<signal.h>
int day; //0表示黑天,1表示白天
pthread_cond_t day_cd, night_cd; //线程挂在条件变量上
pthread_mutex_t lock;
void jobs(void)
{
printf("thread 0x%x day [%d] ,执行巡逻任务\n",(unsigned int)pthread_self(),day);
}
void * thread_night(void * arg)
{
while(1){
pthread_mutex_lock(&lock);
if(day == 1) //如果是白天我就挂起
pthread_cond_wait(&night_cd,&lock); //线程挂在条件变量上,并解锁
//被唤醒了上锁
jobs();
++day;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&day_cd);
usleep(100000);
}
}
void * thread_day(void * arg)
{
while(1){
pthread_mutex_lock(&lock);
if(day == 0)
pthread_cond_wait(&day_cd,&lock); //挂起解锁
//唤醒上锁
jobs();
--day;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&night_cd);
usleep(100000);
}
}
int main(void)
{
pthread_t tid;
pthread_cond_init(&day_cd,NULL);
pthread_cond_init(&night_cd,NULL);
pthread_mutex_init(&lock,NULL);
pthread_create(&tid,NULL,thread_day,NULL);
pthread_create(&tid,NULL,thread_night,NULL);
while(1)
sleep(1);
return 0;
}
生产者消费者: