首先通过一个简单的故事让大家了解什么是线程模型?
有兄弟三人开车出去自驾游, 旅途非常遥远,需要好几天的车程。所以需要三个人轮流开车,这个时候可以一个人开车另外两个人可以去睡觉了,这样大家轮流开车。当开车司机想睡觉的时候可以通过轻轻推醒一个人来接替开车另一个仍然可以接着睡,如果开车司机不高兴也可以通过大声说话的方式,将两个人都叫醒,但是也只能有一个人来开车。
这个故事可以用来比喻线程编程模型思想。这里的司机就是线程,汽车可以理解为共享数据,轻推和大声说话就是通信机制,个体在等待这些事件的发生。
多线程模型具有以下优点:
<1> 在多处理器系统中开发程序的并行性。除了并行性这一优点是需要特殊硬件支持外,其他优点对硬件不做要求。
在多处理器系统中,线程模式可以让一个进程同时执行多个独立运算。一个运行在双CPU上的计算密集型多线程程序几乎可以获得传统的单线程程序两倍的性能。对于并行能带来多大的性能提升,可以Amdahl法则预测:
![](https://img-blog.csdnimg.cn/7ae28128f79a43e38fc6ca896f22c336.png)
在Amdahl法则登时中,p代表可并行代码与整个执行时间的比率,n代表代码可以使用的处理器的数目。并行工作真个延续时间就等于非并行时间(1-p)的延续时间加上每个处理器执行并行工作(p/n)的延续时间。
<2> 在等待慢速外设I/O操作结束的同时,程序可以执行其他计算,为程序的并发提供更有效,更自然的开发方式。
如果事件不是并发的,程序一次只能做一件事情,如大型数据库排序,用户界面可能相当长的时间内无法响应用户的操作;如果实践引发长时间等待,如通过低速网络连续读取数据,用户也只能再次等待。另一方面,你可以创建一个转么排序数据库的线程,或者从网上读取数据而让用户界面线程立即处理另一用户操作。慢速操作继续执行,而程序还可以相应。
<3> 一种模块化编程模型,能清晰地表达程序中独立事件的相互关系。
即使你的代码从不在多处理器系统上运行,了解线程模型仍然很有意义。线程模型将独立的或者松耦合的功能执行流(线程)显示的分离。如果活动设计为线程,那么每个函数必须包括显示的同步以确保依赖关系。因为同步机制就是可执行的代码,所以依赖性改变时也无法忽略他。同步结构的存在阅读代码的人了解代码中的时间依赖关系,使代码维护更加容易,尤其是对于包含大量独立代码的大型程序而言。
多线程模型的缺陷:
<1> 计算负荷
线程代码中的负荷代价包括由于线程间同步所导致的。在几乎任何线程代码中你都需要使用某种同步机制,使用太多的同步很容易损失性能。
<2> 编程规则
尽管线程编程模型的基本思想简单,但是编写实际的代码不是件容易的事。编写能够在多个线程中良好工作的代码需要认真思考和计划。你需要明白同步协议和程序中的不变量,你不得不避免死锁,竞争和优先级倒置。
<3> 更难调试
提供线程功能的系统通常将传统的串行调试工具扩展以提供基本的线程调试支持。系统会提供一个调试器,允许你看到所有线程的条用结构树,并设置只能在特定线程内激活的断点。系统可能提供某种形式的性能分析器,让你计算某个线程或者所有线程中函数的累计占有处理器时间。不幸的是,这仅仅是调试异步代码的开始,调试不可避免的改变事件的顺序。这在调试串行代码时不会有什么太大问题,但是调试异步代码时却是致命的。
何时使用多线程:
最适合使用线程的是实现以下功能的应用:
<1> 计算密集型应用,为了能在多处理器系统上运行,将这些计算分解到多个线程中实现;
<2> I/O密集型应用,为提高性能,将I/O操作重叠。很多线程可以同时等待不同的I/O操作。
多线程的创建和使用:
<1> 创建线程
#include <pthread.h>
pthread_create(thread, attr, start_routine, arg)
参数:
thread 指向线程标识符指针。
attr 一个不透明的属性对象,可以用来设置线程属性。默认值是NULL。
start_routine 线程运行函数起始地址,一旦线程被创建就会运行。
arg 运行函数的参数。
<2> 终止线程
#include <pthread.h>
pthread_exit(status)
#include
<
iostream
>
// 必须的头文件
#include
<
pthread.h
>
using
namespace
std
;
#define
NUM_THREADS
5
// 线程的运行函数
void
*
say_hello
(
void
*
args
)
{
cout
<<
"
Hello Runoob!
"
<<
endl
;
return
0
;
}
int
main
(
)
{
// 定义线程的 id 变量,多个变量使用数组
pthread_t
tids
[
NUM_THREADS
]
;
for
(
int
i
=
0
;
i
<
NUM_THREADS
; ++
i
)
{
//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
int
ret
=
pthread_create
(
&
tids
[
i
]
,
NULL
,
say_hello
,
NULL
)
;
if
(
ret
!=
0
)
{
cout
<<
"
pthread_create error: error_code=
"
<<
ret
<<
endl
;
}
}
//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
pthread_exit
(
NULL
)
;
}
<3>连接和分离线程
pthread_join(threadid, status);
pthread_detach(threadid);
pthread_join()子程序阻碍调用程序,直到指定的threadid线程终止为止。当创建一个线程时,它的属性
会定义它是否可连接的或可分离的。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定
义为可分离的,则永远也不能被连接。
#include
<
iostream
>
#include
<
cstdlib
>
#include
<
pthread.h
>
#include
<
unistd.h
>
using
namespace
std
;
#define
NUM_THREADS
5
void
*
wait
(
void
*
t
)
{
int
i
;
long
tid
;
tid
=