线程:轻量级的进程,是调度的最小单位,
线程是共享同一进程地址空间多个可独立被调度运行的任务
一 多线程 与 多进程的区别
1.在一个进程中创建的多个线程,共享同一个进程的资源,各线程独立被内核调度
2.多个进程是独立地址空间
相同点:
1.都参与统一的调度
2.都有自己的ID,一组寄存器的值
不同点:线程间共享统一个进程的地址空间 ,进程间是独立地址空间
二 线程间共享资源和私有资源
共享资源:全局变量,打开文件获得文件描述符
私有资源:私有TID,私有栈
所有的程序都有一个主线程(main thread),主线程是进程的控制流或执行线程。
在多线程程序中,主线程可以创建一个或多个对等线程(peer thread),
从这个创建时间点开始,这些线程就开始并发执行。
主线程和对等线程的区别其中之一是,主线程总是进程中第一个运行的线程。
三 线程的操作
NPTL 线程库中函数大多以pthread_开头
调用NPTL中函数需要加上 pthread.h头文件
编译和链接需加上 -pthread
1.创建一个线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void * ( *start_routine ) (void *), void *arg);
功能:创建一个线程
参数:
thread 获得线程ID
attr 线程的属性,通常为NULL表示使用默认属性
start_routine 线程的执行函数,即线程执行的代码,采用回调方式执行线程函数
arg 给线程函数传递的参数
返回值:
成功返回0,失败返回错误码
注意:pthread_t为unsigned long类型
例:
void *thread_func(void *arg);
pthread_t tid;
int val = 10;
int ret = 0;
ret = pthread_creaet(&tid,NULL,thread_func,&val);
if(ret != 0)
{
/*errno = ret;*/
/*perror();*/
fprintf(stderr,"pthread_create : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
建议用法
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
s = pthread_create(&thr, NULL, &thread_func, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
pthread_t pthread_self(void);
功能:获得当前线程的tid ,类似进程中的getpid(2),该函数总是成功
2.线程退出
void pthread_exit(void *retval);
功能:结束当前线程的执行
参数:
retval 给等待的线程带回一个地址值 ,如果没有值带回,写为NULL
返回值:无
3.线程连接
等待线程退出,作用类似进程中的wait(2)系统调用,可实现线程同步
int pthread_join(pthread_t thread, void **retval);
功能:阻塞方式等待指定的线程退出,并且释放结束线程未释放的资源(例如:线程的私有栈资源等)
参数:
thread 线程TID
retval 获得pthread_exit带回的地址值
返回值:
成功返回0,失败返回错误码
注意:如果等待的线程没有退出,则调用pthread_join的线程会阻塞
4.线程分离。
通知系统当线程结束,可以自动回收线程未释放的资源
如果线程没有终止,pthread_detach()函数也不会令其终止。
int pthread_detach(pthread_t thread);
5.请求取消一个线程执行
int pthread_cancel(pthread_t thread);
四 线程间的机制
1.线程同步
同步:多个线程按照约定的顺序相互配合完成一件事情
POSIX 线程间同步机制:无名信号量
信号量 :它代表一类资源,其值代表资源的个数,是一个受保护的变量,不能直接赋值
只有三种操作方法:初始化信号量,p操作,v操作
用sem_t类型描述信号量
(1)定义一个信号量
sem_t read_sem;
sem_t write_sem;
(2)初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化指定的信号量
参数:
sem 信号量的地址
pshared 0:线程间使用 非0:进程间使用
value 初始化的值
返回值:成功返回0;失败返回-1,并置errno
(3)P操作 : 申请资源
int sem_wait(sem_t *sem);
功能:申请资源,如果没有资源,则引起调用者阻塞
参数:
sem 信号量的地址
返回值:
成功返回0,失败返回-1,并置errno
(4)V操作 : 释放资源
int sem_post(sem_t *sem);
功能:释放资源,如果有等待资源的线程,则唤醒第一个等待的线程
参数:
sem 信号量的地址
返回值:
成功返回0,失败返回-1
2.线程间互斥
互斥:保证某一个时刻只有一个线程访问资源。
POSIX 线程间互斥机制:互斥锁,保证操作临界资源完整性,防止竞争
用pthread_mutex_t 类型描述互斥锁
(1).临界资源 : 一段时间内只能有一个任务访问,应该加锁保护
(2).临界区 : 访问临界资源的一段代码,即影响共享数据的代码段
互斥锁操作:
(1)定义互斥锁
pthread_mutex_t lock;
(2)初始化互斥锁
//动态初始化:指定锁的属性
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
//静态初始化:锁只能使用默认的属性,注意只能在定义时使用!
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
(3)申请互斥锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
特点:如果互斥锁被别的线程的使用,则阻塞调用者
(4)释放互斥锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
特点:如果互斥锁被别的线程的使用,则阻塞调用者
(5)销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
3.线程间的条件变量
条件变量可以使线程睡眠等待某种条件出现。
条件变量是利用线程间共享的全局变量进行同步的一种机制,
主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;
另一个线程使"条件成立"(给出条件成立信号),从而唤醒等待的线程.
为了防止竞争,条件变量总是和一个互斥锁结合在一起使用。
一般说来,条件变量被用来进行线程间的同步。
条件变量用在某个线程需要在某种条件才去保护它将要操作的临界区的情况下,
从而避免了线程不断轮询检查该条件是否成立而降低效率的情况,提高效率
用pthread_cond_t类型描述条件变量
(1)定义一个条件变量
pthread_cond_t cond;
(2)初始化条件变量
//动态初始化
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
//静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
(3)让线程等待一个条件
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
功能:等待一个条件
参数:
cond 等待的条件
mutex 互斥锁
返回值:
成功返回0, 失败返回错误码
注意:
pthread_cond_wait 内部实现:
1.条件不满足,释放锁,然后阻塞
2.条件满足,获得锁,然后返回
(4)唤醒等待条件线程
//唤醒等待队列中所有等待该条件的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒第一个等待条件的线程
int pthread_cond_signal(pthread_cond_t *cond);