线程是任务调度和执行的基本单位
为什么会有线程
程实现多任务的缺点
进程间切换的计算机资源开销很大,切换效率很低;进程间数据及共享额开销很大
线程和进程的关系
- 线程是进程的一个执行单元,是进程内的调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程
- 同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间
- 进程退出,进程中所有线程全部退出
- 一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮
- 线程不可能完全替代进程
- 线程拥有独立的属性:独立的ID(TID),独立的切换状态,调度优先级,独立函数栈等等
线程的特点
线程切换的开销很低——实质是函数的切换
线程通信机制简单——全局变量
线程操作
注意:线程函数不是OS提供,而是线程库而是线程库libpthread.a/.so,但是线程的核心实现是离不开OS支持的
线程创建
API:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
voi * 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;
}
线程退出
被动退出
API:int pthread_cancel(pthread_t thread);
必须要有系统调用
主动退出
- int pthread_exit(void *retval); 弹栈
如果返回值很多时,就封装成一个结构体,返回结构体变量的地址即可。 - return 返回 ;不弹栈
线程等待
目的:
- 保证线程的退出顺序:保证一个线程退出并且回收资源后允许下一个进程退出
- 回收线程退出时的资源情况:保证当前线程退出后,创建的新线程不会复用刚才退出线程的地址空间
- 获得新线程退出时的结果是否正确的退出返回值,这个有点类似回收僵尸进程的wait,保证不会发生内存泄露等问题
API:int pthread_join(pthread_t thread, void **retval);
线程状态
可结合态:这种状态下的线程是能够被其他进程回收其资源或杀死的
分离态:这种状态下的线程是不能够被其他线程回收或杀死的;它的存储资源在它终止时由系统自动释放
默认情况下,线程被创建成可结合态
线程同步
线程VS进程
进程:进程空间天然是独立的,因此进程间资源的保护是天然的(现成的),需要重点关心的进程间的通信
线程:多线程天然的共享进程空间,因此线程数据共享是天然的(现成的),需要重点关心的是资源的保护
互斥锁(必须设置为全局变量)
- 定义一个互斥锁(pthread_mutex_t mutex)
- 初始化互斥锁(pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER)
- 加锁解锁(阻塞加锁,非阻塞加锁,访问临界区解锁)
- 进程退出时销毁互斥锁(删除互斥锁相关的数据,释放互斥锁数据所占用的各种内存资源。)
线程信号量
- 定义信号量结合(sem_t sem[n],线程信号量集合其实就是一个数组,数组每个元素就是一个信号量。)
- 初始化集合中的每个信号量,int sem_init(sem_t *sem, int pshared, unsigned int value);
- p、v操作( int sem_wait(sem_t *sem)//阻塞p操作;int sem_post(sem_t *sem))
- 进程结束时,删除线程信号量集合( int sem_destroy(sem_t *sem);)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<semaphore.h>
struct message
{
int fd;
};
sem_t sem[1];
void * thread1(void *arg)
{
struct message msg = *((struct message * )arg);
int fd= msg.fd;
while(1)
{
sem_wait(&sem[0]);
write(fd,"111\n",4);
sem_post(&sem[0]);
}
}
void * thread2(void *arg)
{
struct message msg = *((struct message * )arg);
int fd= msg.fd;
while(1)
{
sem_wait(&sem[0]);
write(fd,"222\n",4);
sem_post(&sem[0]);
}
}
void *thread3(void *arg)
{
struct message msg = *((struct message * )arg);
int fd= msg.fd;
while(1)
{
sem_wait(&sem[0]);
write(fd,"333\n",4);
sem_post(&sem[0]);
}
}
int main()
{
pthread_t id1;
pthread_t id2;
pthread_t id3;
struct message msg;
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;
sem_init(&sem[0],0,1);
if(pthread_create(&id1,NULL,thread1,(void *)(&msg)) != 0)
{
perror("thread create error!\n");
exit(1);
}
if(pthread_create(&id2,NULL,thread2,(void *)(&msg)) != 0)
{
perror("thread create error!\n");
exit(1);
}
if(pthread_create(&id3,NULL,thread3,(void *)(&msg)) != 0)
{
perror("thread create error!\n");
exit(1);
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
return 0;
}
条件变量
多线程配合工作时,当线程检测到某条件不满足时就休眠,直到别的线程将条件准备好,然后通过条件变量将其唤醒。
条件变量需要在互斥锁的配合下才能工作
- 定义一个条件变量(全局变量)由于条件变量需要互斥锁的配合,所以还需要定义一个线程互斥锁(pthread_cond_t)
- 初始化条件变量(int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);)
- 使用条件变量( int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); )
- 删除条件变量,也需要把互斥锁删除(int pthread_cond_destroy(pthread_cond_t *cond);)
#include<stdio.h>
#include<stdlib.h>
#include<pthread.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);
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 creare error!");
exit(1);
}
ret=pthread_create(&id2,NULL,print_1,NULL);
if(ret!=0)
{
perror("thread creare error!");
exit(1);
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}