Linux 线程
一、基础概念
1.线程:一个程序执行的多个执行路线称为线程。轻量级别的进程,程序执行的最小单元;
2.特点:多个线程共享一个进程的地址空间;
二、线程创建使用相关函数
1.int pthread_create(pthread_t * thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);
//返回值:成功返回0 ,失败返回错误码,不一定置errno;
//参数:
//thread 获得线程ID
//attr 线程的属性(分离属性和结合属性),通常为NULL表示使用默认属性(结合属性)
//start_routine 线程的执行函数,采用回调方式执行线程函数
//arg 给线程函数传递的参数
//练习:主线程与子线程多数据交换,以及主线程改变子线程的数据
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
struct data1
{
int int_data;
short short_data;
char char_data;
};
int global=0;
void *func1(void *arg1)
{
struct data1 getData=*(struct data1 *)arg1;
while(1)
{
sleep(1);
printf("int_data:%d short_data :%d char_data :%d\n",getData.int_data,getData.short_data,getData.char_data);
global++;
}
}
void *func2(void *arg2)
{
int *gDtata=(int *)arg2;
while(1)
{
sleep(1);
printf("global:%d data:%d\n",global,*gDtata);
(*gDtata)++;
}
}
int main(int argc, const char *argv[])
{
pthread_t tid,tid1;
int ret;
int data=10;
struct data1 past_data={20,40,60};
ret=pthread_create(&tid,NULL,func1,&past_data);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
ret=pthread_create(&tid1,NULL,func2,&data);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
while(1)
{
printf("main running:%d\n",data);
sleep(1);
}
return 0;
}
2.线程终止
void pthread_exit(void *retval);//
//结束当前线程的执行
//参数:
/retval //给等待的线程带一个地址值,如果无则填NULL;
(1)如果在主线程中调用了return 或者exit()函数,则进程结束,所有线程均结束;
(2)如果主线程调用了pthread_exit()函数,则仅仅是主线程消亡,主线程等所有子线程结束时才会结束。
//示例:主线程程中return 前调用pthread_exit()函数,等子线程结束,主进程才结束
#include<stdio.h>
#include<pthread.h>
void *func(void *arg)
{
int data=*(int *)arg;
while(1)
{
sleep(1);
if(data==4)
{
break;
}
printf("thread 1 value:%d\n",data++);
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
int ret;
int value=0;
pthread_t tid;
ret =pthread_create(&tid,NULL,func,&value);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
pthread_exit(NULL);
return 0;
}
3.等待线程退出函数
(1)int pthread_join(pthread_t thread,void ** pval);//阻塞的方式等待线程结束
//通过pthread_exit(&value);传回线程内部指针,可以带回返回值给二级指针pval,但是,如果传回的是普通变量的值,则在该线程结束后局部变量被回收,值就没有了,可以把传回值value 设置成static 静态变量。
//示例:
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
void *func(void *arg)
{
int data=*(int *)arg;
static int value=30;
while(1)
{
sleep(1);
if(data==4)
{
break;
}
printf("thread 1 value:%d\n",data++);
}
printf("thread 1 value:%p\n",&value);
pthread_exit(&value);
}
int main(int argc, const char *argv[])
{
int ret;
int value=0;
int *pval=NULL;
pthread_t tid;
ret =pthread_create(&tid,NULL,func,&value);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
ret=pthread_join(tid,(void **)&pval);
if(ret!=0)
{
fprintf(stderr,"Fail to pthread_join %s\n",strerror(errno));
return -1;
}
printf("pval=%p value:%d\n",pval,*pval);
pthread_exit(NULL);
return 0;
}
4.创建分离式线程
int pthread_detach(pthread_t thread);//非阻塞
//功能:将线程标记为分离式线程
//参数:
//thread 线程的tid号
//返回值:成功返回0,失败返回错误码
(1)分离式线程的特点:线程结束时,系统会自动回收资源,不需要pthread_join()函数来阻塞回收;
//示例:
//创建线程后,在主线程添加
pthread_detach(tid);//把需要改成分离式线程的线程号填进去,这里看不到效果,子线程结束后,系统自动回收资源。
5.取消一个线程
int pthread_cancel(pthread_t thread);//取消一个线程的执行(取消别人)
//参数:
//thread 线程的tid号
//返回值:成功返回0,失败返回错误码
//示例:创建子线程,主线程休眠4秒取消子线程,并回收子线程资源。
#include<stdio.h>
#include<pthread.h>
void *func(void *arg)
{
while(1)
{
sleep(1);
printf("thread 1 running\n");
}
pthread_exit(NULL);//
}
int main(int argc,const char *argv[])
{
pthread_t tid;
int ret;
ret=pthread_create(&tid,NULL,func,NULL);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
sleep(4);
ret=pthread_cancel(tid);//取消子线程,成功返回0,失败返回错误码
if(ret!=0)
{
perror("Fail to pthread_cance");
return -1;
}
pthread_exit(NULL);
}
//示例:打开一个文件,创建两个子进程,一个子进程写,一个子进程读并且打印,当读到"quit"时,退出子进程。
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int gTid;//其实可以通过传参把读线程的线程号传入写线程
void *func1(void *arg1)//read
{
char buf[1024]={0};
int fd=*(int *)arg1;
while(1)
{
memset(buf,0,sizeof(buf));
ssize_t ret=read(fd,buf,sizeof(buf));
if(ret<0)
{
perror("Fail to read");
return NULL;
}
else if(ret==0)
{
continue;
}
else
{
printf("p:%s\n",buf);
}
}
pthread_exit(NULL);
}
void *func2(void *arg2)//write
{
char buf[1024]={0};
int fd=*(int *)arg2;
while(1)
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strncmp(buf,"quit",4)==0)
{
pthread_cancel(gTid);
break;
}
write(fd,buf,strlen(buf));
lseek(fd,-strlen(buf),SEEK_CUR);
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
pthread_t tid1,tid2;
int ret;
int fd;
if(argc!=2)
{
fprintf(stderr,"usage: %s log.txt",argv[0]);
return -1;
}
fd=open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666);
if(fd<0)
{
perror("Fail to open");
return -1;
}
ret=pthread_create(&tid1,NULL,func1,&fd);//read
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
gTid=tid1;
ret=pthread_create(&tid2,NULL,func2,&fd);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
pthread_exit(NULL);
return 0;
}
6.线程间同步,使用信号量
//功能:对于两个线程,创建两个(全局变量)信号量A,B(sem_t A,B),先对A,B赋初值,A 为1(sem_init(&A,0,1),B为0(sem_init(&B,0,0),在第一个线程中使用sem_wait(&A),当A为0时阻塞,当使用sem_post(&A)时,释放A,此时sem_wait(&A)可以顺利执行。
(1)建立全局变量的信号量:sem_t sem_A;
(2)初始化信号量:
int sem_init(sem_t *sem,int pshared,unsigned int value);
//返回值:成功返回0,失败返回-1
//参数:
//sem 第一步创建的 信号量;
//pshared 0:线程间使用
//value 信号量的初始值
(3)P操作
int sem_wait(sem_t *sem);
//功能:申请资源,如果资源为0则阻塞调用者
//返回值:成功返回0,失败返回-1
//参数:
//sem :信号量的地址
(3)V操作
int sem_post(sem_t *sem);
//功能:释放资源,如果有线程等待则唤醒等待的线程
//返回值:成功返回0,失败返回-1
//参数:
//sem :信号量的地址
//示例:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
sem_t sem_read,sem_write;
struct data_t
{
char name[20];
int id;
int score;
};
struct data_t stu;
void *func1(void *arg1)//write
{
char buf[1024]={0};
while(1)
{
sem_wait(&sem_write);
memset(&stu,0,sizeof(struct data_t));
memset(buf,0,sizeof(buf));
printf("name:");
fgets(buf,sizeof(buf),stdin);
fflush(stdin);
buf[strlen(buf)-1]='\0';
strcpy(stu.name,buf);
if((stu.name,"quit",4)==0)
{
break;
}
printf("ID:");
scanf("%d",&stu.id);
getchar();
printf("score:");
scanf("%d",&stu.score);
getchar();//取走换行符
sem_post(&sem_read);
}
pthread_exit(NULL);
}
void *func2(void *arg2)
{
while(1)
{
sem_wait(&sem_read);
printf("name:%s id:%d score:%d\n",stu.name,stu.id,stu.score);
sem_post(&sem_write);//释放信号量
}
}
int main(int argc, const char *argv[])
{
pthread_t tid1,tid2;
int ret;
sem_init(&sem_read,0,0);//初始化信号量
sem_init(&sem_write,0,1);
ret=pthread_create(&tid1,NULL,func1,NULL);
if(ret!=0)
{
perror("Fail to create_thread");
return -1;
}
ret=pthread_create(&tid2,NULL,func2,NULL);
if(ret!=0)
{
perror("Fail to create_thread");
return -1;
}
pthread_exit(NULL);
return 0;
}
7.线程间互斥 --互斥锁
//功能:一个线程访问资源的时候,不允许其他线程访问;
宏创建互斥锁
pthread_mutex_t mutex=
(1)定义互斥锁
pthread_mutex_t mlock;//定义了一个叫mlock的互斥锁
(2)初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
//返回值:成功返回0,失败返回-1
//参数:
//mutex 互斥锁地址
//attr 默认值NULL;
(3)获得互斥锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
//功能:获得互斥锁则立即返回,否则阻塞直到获得互斥锁
//返回值:成功返回0,失败返回-1
//参数: mutex 互斥锁地址
(4)获得互斥锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//功能:释放互斥锁
//返回值:成功返回0,失败返回-1
//参数: mutex 互斥锁地址
(5)销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//功能:销毁互斥锁
//返回值:成功返回0,失败返回-1
//参数: mutex 互斥锁地址
//示例:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
int pro=0;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//定义互斥锁同时初始化
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//定义条件变量同时初始化
void *func1(void *arg)
{
int data;
while(1)
{
pthread_mutex_lock(&mutex);//抢占互斥锁
while(pro!=0)
{
pthread_cond_wait(&cond,&mutex);//
}
data=rand()%100;
pro=data;
printf("producer:%d\n",pro);
pthread_mutex_unlock(&mutex);//释放互斥锁
pthread_cond_signal(&cond);
}
pthread_exit(NULL);
}
void *func2(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);//抢占互斥锁
while(pro==0)
{
pthread_cond_wait(&cond,&mutex);//
}
printf("pro=%d\n",pro);
pro=0;
pthread_mutex_unlock(&mutex);//释放互斥锁
pthread_cond_signal(&cond);
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
pthread_t tid1,tid2;
int ret;
ret=pthread_create(&tid1,NULL,func1,NULL);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
ret=pthread_create(&tid2,NULL,func2,NULL);
if(ret!=0)
{
perror("Fail to pthread_create");
return -1;
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_exit(NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
8.条件变量
(1)定义:条件变量是利用线程间共享的全局变量进行同步通信的一种机制。多个线程等待某一个条件满足,在条件达到时由一个线程去通知其他等待的线程。
(2)条件变量相关函数。
pthread_cont_t cond;//定义条件变量
pthread_cont_init(&cond,NULL);//动态初始化条件变量
//等待条件变量,条件不满足,则阻塞当前进程,
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
//唤醒条件变量
int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒所有等待的线程
int pthread_cond_signal(pthread_cond_t *cond);//唤醒至少一个等待线程
//销毁条件变量
pthread_cond_destroy(pthread_cond_t *cond);
//示例:见互斥锁。