线程是任务调度和执行的基本单位
为什么会有线程?
1、进程实现多任务的缺点
进程间切换的计算机资源开销很大,切换效率非常低
进程间数据共享的开销也很大
2、线程和进程的关系
1)线程是进程的一个执行单元,是进程内的调度实体。
2)同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
3)进程退出,进程中所有线程全部退出;
4)一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮
5)线程不可能完全替代进程
6)线程拥有独立的属性
线程的特点:
线程操作
1、线程创建
2、线程退出
注意:pthread_cancel 有个bug,必须要发生系统调用
pthread_cancel
(1)函数原型
#include <pthread.h>
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 <pthread.h>
int pthread_detach(pthread_t thread);
1)功能
如果次线程的资源不希望别人调用pthread_join函数来回收的话,而是希望自己在结束时,自动回收
资源的话,就可以调用这个函数。
这个函数的功能就是分离次线程,让次线程在结束时自动回收资源。
2)返回值
成功返回0,失败返回错误号。
3)参数
thread:你要分离的那个次线程的TID。
线程同步
初始化互斥锁的函数
(a)函数原型
#include <pthread.h>
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 <semaphore.h> 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 <semaphore.h>
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 <semaphore.h>
int sem_post(sem_t *sem);
· 功能:对某个信号量进行v操作,v操作不存在阻塞问题。
v操作成功后,信号量的值会+1
· 返回值:成功返回0,失败返回-1,errno被设置。
(b)代码演示
sem_post(&sem[0]);
使用条件变量
2)等待条件的函数
(a)函数原型
#include <pthread.h>
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 <pthread.h>
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 <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
代码案例
pthread1.c
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#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=%s\n",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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#define MAXSIZE 1024
char buffer[MAXSIZE];
void *thread1(void *arg)
{
int num=*((int *)arg);
printf("num=%d\n",num);
while(1)
{
// memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
sleep(2);
}
}
void *thread2(void *arg)
{
char *ptr=(char *)arg;
printf("ptr=%s\n",ptr);
while(1)
{
printf("buffer=%s\n",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<stdio.h>
#include<pthread.h>
#include<unistd.h>
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<stdio.h>
#include<pthread.h>
#include<unistd.h>
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=%d\n",*((int*)num));
// pause();
return 0;
}
pthread6.c
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
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("hh\n");
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=%d\n",*((int*)num));
// pause();
return 0;
}
pthread7.c
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
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("%d\n",count);
// sleep(1);
// pthread_mutex_unlock(&mutex);
// sleep(1);
if(count!=5)
{
pthread_cond_wait(&cond,&mutex);
}
printf("count=%d\n",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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#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,"world\n",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,"wwwww\n",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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<semaphore.h>
#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,"world\n",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,"wwwww\n",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线程