线程是任务调度和执行的基本单位
为什么会有线程?
1、进程实现多任务的缺点
进程间切换的计算机资源开销很大,切换效率非常低
进程间数据共享的开销也很大
2、线程和进程的关系
1)线程是进程的一个执行单元,是进程内的调度实体。
2)同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
3)进程退出,进程中所有线程全部退出;
4)一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮
5)线程不可能完全替代进程
6)线程拥有独立的属性
线程的特点:
线程操作
1、线程创建
2、线程退出
注意:pthread_cancel 有个bug,必须要发生系统调用
pthread_cancel
(1)函数原型
#include
int pthread_cancel(pthread_t thread);
1)功能
当次线程是死循环时,可以调动这个函数主动取消该线程。(必须发生系统调用)
2)返回值
成功返回0,失败返回非零错误号。
2)参数
thread:要取消线程的TID
void pthread_exit(void *retval);
1)功能
线程调用这个函数时,可以主动退出(终止)。
这类似于exit函数,不过exit是终止整个进程的,而pthread_exit是终止次线程的。
如果你在次线程里面调用错误,调用的是exit,整个进程就终止了。
2)返回值
成功返回0,失败返回非零错误号。
3)参数
retval:线程结束时的返回值。
如果返回值很多时,就封装成一个结构体,返回结构体变量的地址即可。
3、线程等待
int pthread_join(pthread_t thread, void **retval);
1 )功能:阻塞等待tid为thread的次线程结束,结束时该函数会回收次线程所占用的所有资源(存储空间)。 这个函数只对次线程有意义,对主线程没有意义,因为主线程结束时真个进程就结束了,整个进程资源
会由父进程回收。
这个函数一般都是由主线程调用,以回收相关次线程的资源,当然次线程也是可以调用这个函数来回
收其它次线程资源的。
2)返回值
成功返回0,失败返回错误号。
3)参数
(a)thread:指定要回收次线程的TID
(b)retval:次线程函数返回的返回值
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);
功能
(a)pthread_cleanup_push
将类型为void (*routine)(void *)函数注册为“线程退出处理函数”,arg为传递给退出处理函数的参数。 注册的原理就是将处理函数地址压入线程栈。
我们可以反复调用该函数注册多个退出处理函数,但是一般一个就够了。
(b)pthread_cleanup_pop
执行这个函数时,
· 如果参数写!0:会将压入栈中的推出处理函数地址弹出,然后调用退出函数进行线程的扫尾处理。
· 如果参数写0:不弹出调用
如果注册了多个线程退出处理函数的话,由于栈先进后出的特点,所以注册压栈的顺序与弹
栈调动的顺序刚好相反。
这个函数必须和pthread_cleanup_push配对出现,有一个pthread_cleanup_push,就必须要
对应有一个pthread_cleanup_pop,就算这个函数调用不到也必须写,否者编译时不通过,
这就好比{}是成对出现的,缺一个都会报错。
线程状态
pthread_detach
(1)函数原型
#include
int pthread_detach(pthread_t thread);
1)功能
如果次线程的资源不希望别人调用pthread_join函数来回收的话,而是希望自己在结束时,自动回收
资源的话,就可以调用这个函数。
这个函数的功能就是分离次线程,让次线程在结束时自动回收资源。
2)返回值
成功返回0,失败返回错误号。
3)参数
thread:你要分离的那个次线程的TID。
线程同步
初始化互斥锁的函数
(a)函数原型
#include
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
· 功能:初始化定义的互斥锁
什么是初始化,就是设置互斥锁所需要的值。
· 返回值
总是返回0,所以这个函数不需要进行出错处理。
· 参数
- mutex:互斥锁,需要我们自己定义。
比如:pthread_mutex_t mutex;
pthread_mutex_t是一个结构体类型,所以mutex实际上是一个结构体变量。
加锁解锁
pthread_mutex_lock(&mutex)(阻塞加锁)访问临界区加锁操作
pthread_mutex_trylock( &mutex)(非阻塞加锁); pthread_mutex_lock() 类似,不同的是在锁已经被占据时返回 EBUSY 而不是挂起等待。
pthread_mutex_unlock(&mutex): 访问临界区解锁操
初始化信号量的函数
(a)函数原型
#include int sem_init(sem_t *sem, int pshared, unsigned int value);
· 功能
初始化线程信号量集合中的某个信号量,给它设置一个初始值。
· 返回值
成功返回0,失败返回-1,errno被设置。
注意信号量的错误号不是返回的,而是设置到errno中。
· 参数
- sem:信号量集合中的某个信号量
信号量集合需要我们自己定义,
比如:sem_t sem[3],
线程信号量集合其实就是一个数组,数组每个元素就是一个信号量。
sem[0]:第一个信号量
sem[1]:第二个信号量
sem[2]:第三个信号量
sem_init(&sem[0], int pshared, unsigned int value);
线程信号量集合其实就是自定义的一个数组,而进程信号量集合则是通过semget函数创建的。
我们只要把数组定义为全局变量,所有的线程即可共享使用,不像进程信号量,需要semid
才能实现共享操作。
- pshared:
+ 0:给线程使用
+ !0:可以给进程使用
不过对于进程来说,我们更多的还是使用进程信号量,因为线程信号量用到
进程上时,存在一些不稳定的情况。
- value:初始化值
对于二值信号量来说,要么是1,要么是0。
P、V操作
P操作
(a)函数原型
#include
int sem_wait(sem_t *sem);//阻塞p操作
· 功能:阻塞p操作集合中某个信号量,值-1
如果能够p操作成功最好,否则就阻塞直到p操作操作成功为止。
· 返回值:成功返回0,失败返回-1,errno被设置。
· 参数:p操作的某个信号量。
比如:sem_wait(&sem[0]);
· sem_wait的兄弟函数
int sem_trywait(sem_t *sem):不阻塞
如果能够p操作就p操作,如果不能p操作就出错返回,不会阻塞。
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
可以设置阻塞时间,如果能够p操作就p操作,不能就阻塞,如果在设置的时间内好没有
p操作成功就是出错返回,不再阻塞。
这两个函数了解即可,不需要掌握,如果你真的用到了,自己举一反三即可搞定。
(b)代码演示
4)v操作
(a)函数原型
#include
int sem_post(sem_t *sem);
· 功能:对某个信号量进行v操作,v操作不存在阻塞问题。
v操作成功后,信号量的值会+1
· 返回值:成功返回0,失败返回-1,errno被设置。
(b)代码演示
sem_post(&sem[0]);
使用条件变量
2)等待条件的函数
(a)函数原型
#include
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
· 功能:
检测条件变量cond,如果cond没有被设置,表示条件还不满足,别人还没有对cond进行设置,此时
pthread_cond_wait会休眠(阻塞),直到别的线程设置cond表示条件准备好后,才会被唤醒。
· 返回值:成功返回0,失败返回非零错误号
· 参数
- cond:条件变量
- mutex:和条件变量配合使用的互斥锁
(c)pthread_cond_wait的兄弟函数
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
多了第三个参数,用于设置阻塞时间,如果条件不满足时休眠(阻塞),但是不会一直休眠,
当时间超时后,如果cond还没有被设置,函数不再休眠。
3)设置条件变量的函数
(a)函数原型
#include
int pthread_cond_signal(pthread_cond_t *cond);
· 功能
当线程将某个数据准备好时,就可以调用该函数去设置cond,表示条件准备好了,
pthread_cond_wait检测到cond被设置后就不再休眠(被唤醒),线程继续运行,使用别的线程
准备好的数据来做事。
当调用pthread_cond_wait函数等待条件满足的线程只有一个时,就是用pthread_cond_signal
来唤醒,如果说有好多线程都调用pthread_cond_wait在等待时,使用
int pthread_cond_broadcast(pthread_cond_t *cond);
它可以将所有调用pthread_cond_wait而休眠的线程都唤醒。
(b)代码演示
调用pthread_cond_signal去设置条件变量,相当是给pthread_cond_wait发送了一个线程间专
用的信号,通知调用pthread_cond_wait的线程,某某条件满足了,不要再睡了,赶紧做事吧。
删除条件变量 ,也需要把互斥锁删除
(a)函数原型
#include
int pthread_cond_destroy(pthread_cond_t *cond);
代码案例
pthread1.c
#include#include #include #include #include void *thread1(void *arg) { while(1) { printf("hello world!n"); sleep(1); } } int main() { pthread_t id; if(pthread_create(&id,NULL,thread1,NULL)!=0) { perror("pthread create error!n"); exit(1); } pause();//挂起进程 return 0; }
pthread2.c
#include#include #include #include #include #define MAXSIZE 1024 char buffer[MAXSIZE]; void *thread1(void *arg) { while(1) { // memset(buffer,0,sizeof(buffer)); scanf("%s",buffer); sleep(2); } } void *thread2(void *arg) { while(1) { printf("buffer=%sn",buffer); sleep(2); } } int main() { pthread_t id1; if(pthread_create(&id1,NULL,thread1,NULL)!=0) { perror("pthread1 create error!"); exit(1); } pthread_t id2; if(pthread_create(&id2,NULL,thread2,NULL)!=0) { perror("pthread2 create error!"); exit(1); } pause(); return 0; }
pthread3.c
#include#include #include #include #include #define MAXSIZE 1024 char buffer[MAXSIZE]; void *thread1(void *arg) { int num=*((int *)arg); printf("num=%dn",num); while(1) { // memset(buffer,0,sizeof(buffer)); scanf("%s",buffer); sleep(2); } } void *thread2(void *arg) { char *ptr=(char *)arg; printf("ptr=%sn",ptr); while(1) { printf("buffer=%sn",buffer); sleep(2); } } int main() { pthread_t id1; int num =5; char *ptr ="hello world"; if(pthread_create(&id1,NULL,thread1,(void *)(&num))!=0) { perror("pthread1 create error!"); exit(1);。、 } pthread_t id2; if(pthread_create(&id2,NULL,thread2,(void *)ptr)!=0) { perror("pthread2 create error!"); exit(1); } pause(); return 0; }
pthread4.c
#include#include #include int i=0; void *print(void *arg) { while(1) { i++; printf("hello world!n"); sleep(2); } } int main() { pthread_t id; pthread_create(&id,NULL,print,NULL); sleep(3); pthread_cancel(id); pthread_join(id,NULL); printf("thread is exit!n"); // pause(); return 0; }
pthread5.c
#include#include #include int i=0; int num =5; void *print(void *arg) { while(1) { i++; printf("hello world!n"); sleep(2); pthread_exit((void *)(&num)); } } int main() { pthread_t id; pthread_create(&id,NULL,print,NULL); sleep(3); // pthread_cancel(id); void *num; pthread_join(id,&num); printf("thread is exit=%dn",*((int*)num)); // pause(); return 0; }
pthread6.c
#include#include #include int i=0; int num =5; void thread_exit(void *arg) { printf("my exit!n"); } void *print(void *arg) { pthread_cleanup_push(thread_exit,NULL); while(1) { i++; printf("hhn"); sleep(2); // pthread_exit((void *)(&num)); pthread_exit(NULL); // return (void *)0;//不会弹栈 } pthread_cleanup_pop(!0); } int main() { pthread_t id; pthread_create(&id,NULL,print,NULL); pthread_detach(id); sleep(3); // pthread_cancel(id); void *num; pthread_join(id,&num); // printf("thread is exit=%dn",*((int*)num)); // pause(); return 0; }
pthread7.c
#include#include #include #include int count=0; pthread_mutex_t mutex; pthread_cond_t cond; void* add_1(void *arg) { while(1) { pthread_mutex_lock(&mutex); count++; if(count==5) { pthread_cond_signal(&cond); } sleep(1); pthread_mutex_unlock(&mutex); sleep(1); } } void* print_1(void *arg) { while(1) { pthread_mutex_lock(&mutex); // printf("%dn",count); // sleep(1); // pthread_mutex_unlock(&mutex); // sleep(1); if(count!=5) { pthread_cond_wait(&cond,&mutex); } printf("count=%dn",count); count=0; pthread_mutex_unlock(&mutex); sleep(1); } } int main() { pthread_t id1; pthread_t id2; int ret; pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cond,NULL); ret = pthread_create(&id1,NULL,add_1,NULL); if(ret!=0) { perror("thread create error!"); exit(1); } ret =pthread_create(&id2,NULL,print_1,NULL); if(ret!=0) { perror("thread create error!"); exit(1); } pthread_join(id1,NULL); pthread_join(id2,NULL); return 0; }
pthreadfile.c
#include#include #include #include #include #include #include #include #define MAXSIZE 1024 struct message { int fd; pthread_mutex_t mutex;//尽量写成全局 }; pthread_mutex_t mutex; char buffer[MAXSIZE]; void *thread1(void *arg) { struct message msg=*((struct message *)arg); int fd=msg.fd; while(1) { pthread_mutex_lock(&mutex); write(fd,"hello",5); write(fd,"worldn",6); // sleep(2); pthread_mutex_unlock(&mutex); } } void *thread2(void *arg) { struct message msg=*((struct message *)arg); int fd=msg.fd; while(1) { pthread_mutex_lock(&mutex); write(fd,"hhhhh",5); write(fd,"wwwwwn",6); // sleep(2); pthread_mutex_unlock(&mutex); } } int main() { pthread_t id1; struct message msg={.mutex=PTHREAD_MUTEX_INITIALIZER}; int fd; fd=open("./a.txt",O_RDWR|O_CREAT|O_APPEND,0644); if(fd<0) { perror("open file error!"); exit(1); } msg.fd=fd; pthread_mutex_init(&mutex,NULL); if(pthread_create(&id1,NULL,thread1,(void *)(&msg))!=0) { perror("pthread1 create error!"); exit(1); } pthread_t id2; if(pthread_create(&id2,NULL,thread2,(void *)(&msg))!=0) { perror("pthread2 create error!"); exit(1); } // pause(); pthread_join(id1,NULL); pthread_join(id2,NULL); return 0; }
pthreadfile1.c//信号量的使用,结果同上
#include#include #include #include #include #include #include #include #include #define MAXSIZE 1024 struct message { int fd; //pthread_mutex_t mutex;//尽量写成全局 }; pthread_mutex_t mutex; sem_t sem[1]; char buffer[MAXSIZE]; void *thread1(void *arg) { struct message msg=*((struct message *)arg); int fd=msg.fd; while(1) { // pthread_mutex_lock(&mutex); sem_wait(&sem[0]); write(fd,"hello",5); write(fd,"worldn",6); // sleep(2); // pthread_mutex_unlock(&mutex); sem_post(&sem[0]); } } void *thread2(void *arg) { struct message msg=*((struct message *)arg); int fd=msg.fd; while(1) { // pthread_mutex_lock(&mutex); sem_wait(&sem[0]); write(fd,"hhhhh",5); write(fd,"wwwwwn",6); // sleep(2); // pthread_mutex_unlock(&mutex); sem_post(&sem[0]); } } int main() { pthread_t id1; struct message msg={.mutex=PTHREAD_MUTEX_INITIALIZER}; int fd; fd=open("./a.txt",O_RDWR|O_CREAT|O_APPEND,0644); if(fd<0) { perror("open file error!"); exit(1); } msg.fd=fd; pthread_mutex_init(&mutex,NULL); sem_init(&sem[0],0,1); if(pthread_create(&id1,NULL,thread1,(void *)(&msg))!=0) { perror("pthread1 create error!"); exit(1); } pthread_t id2; if(pthread_create(&id2,NULL,thread2,(void *)(&msg))!=0) { perror("pthread2 create error!"); exit(1); } // pause(); pthread_join(id1,NULL); pthread_join(id2,NULL); return 0; }
进程VS线程



