之前的博客中有介绍过线程,但有些内容不够完整,这篇文章是对线程的总结以及介绍线程安全问题。
线程的优点:
(1)创建一个新线程的代价要比创建一个新进程小的多
(2)与进程之间的切换相比,线程之间的切换需要操作系统所做的工作少的多
(3)线程占用资源要比进程少的多
(4)能充分利用多处理器的可并行数量
(5)在等待慢速I/O操作的同时,程序可执行其他操作
(6)计算密集型数据,为了能在多处理器机器上运行,将计算分解到多个线程中实现
(7)I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以等待不同的I/O操作。
线程的缺点:
(1)性能缺失
一个很少被外部事件阻塞的计算密集型线程往往无法同其他线程共享同一个处理器。如果计算密集型线程的数量比处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
(2)健壮性降低
编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,即线程之间是缺乏保护的。
(3)缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些os函数会对整个进程造成影响。
(4)编程难度提高
编写与调试一个多线程程序比编写一个单线程程序难的多
线程终止:
如果需要只终止某个线程而不终止整个进程,有以下三种方法:
(1)在线程函数return.这种方法对主线程不适用,从main函数return相当于调用exit.
(2)调用pthread_exit终止自己
(3)一个线程调用pthread_cancle取消另一个线程
void pthread_exit(void* value_ptr);
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的。不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时,线程函数已经退出了。
为什么需要线程等待?
(1)已经退出的线程,其空间没有被释放,仍然在进程的地址空间内
(2)创建新的线程不会复用刚才退出的线程的地址空间
int pthread_join(pthread_t thread,void** value_ptr);
thread:线程id
value_pthr:指向一个指针,后者指向线程的返回值。
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的。
(1)如果thread通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值
(2)如果thread线程被别的线程调用pthread_cancel异常终止,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED
(3)如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元里存放的是pthread_exit的参数
(4)如果对thread线程的终止状态不感兴趣,则可以传NULL给value_ptr参数。
互斥量
多线程编程中,多个线程之间需要共享一些全局变量,这就可能造成程序运行结果和我们预期的不符,因为线程之间互相干扰。要保证程序的正确性,必须给临界区加锁。此时我们可以用到互斥量。
使用互斥量加锁的流程如下:
初始化互斥量的两种方法:
(1)静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER
(2)动态分配
int pthread_mutex_init(pthread_mutex_t* restrict mutex,const pthread_mutexattr_t *restrict attr);
//mutex:要初始化的互斥量
//attr:NULL
销毁互斥量:
销毁互斥量时需要注意:
(1)使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁
(2)不要销毁一个已经加锁的互斥量
(3)已经销毁的互斥量,要确保后面不会有线程再尝试加锁
int pthread_mutex_dextroy(pthread_mutex_t* mutex);
调用pthread_lock时,可能会遇到以下情况:
(1)互斥量处于未锁状态,该函数将互斥量锁定,同时成功返回
(2)发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread_lock调用会陷入阻塞,等待互斥量解锁。
pthread_mutex_trylock:如果发现加了锁,就非阻塞返回错误码。
条件变量
当一个线程互斥的访问某个变量时,它可能发现在其他线程改变状态之前,它什么也做不了。
这种保证线程之间先后顺序的机制就是条件变量。
有关条件变量的函数:
(1)初始化
int pthread_cond_init(pthread_cond_t* restrict cond,const pthread_condattr_t* restrict attr);
//cond:要初始化的条件变量
//attr:NULL
(2)销毁
int pthread_cond_detroy(pthread_cond_t *cond);
(3)等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
//cond:要在这个条件变量上等待
//mutex:互斥量
(4)唤醒等待
int pthread_cond_broadcast(pthread_cond_t* cond);
int pthread_cond_signal(pthread_cond_t* cond);
多线程编程的典型案例:
生产者消费者模型
http://blog.csdn.net/chaseraod/article/details/74624733
读者写者模型
http://blog.csdn.net/chaseraod/article/details/74856871