尽管面临很多挑战多线程有一些优点使得它一直被使用这些优点是 ● 资源利用率更好 ● 程序设计在某些情况下更简单 ● 程序响应更快 资源利用率更好 想象一下一个应用程序需要从本地文件系统中读取和处理文件的情景比方说从磁盘读取一个文件需要秒处理一个文件需要秒处理两个文件则需要 秒读取文件A 秒处理文件A 秒读取文件B 秒处理文件B
总共需要秒从磁盘中读取文件的时候大部分的CPU时间用于等待磁盘去读取数据在这段时间里CPU非常的空闲它可以做一些别的事情通过改变操作的顺序就能够更好的使用CPU资源看下面的顺序 秒读取文件A 秒读取文件B + 秒处理文件A 秒处理文件B
总共需要秒CPU等待第一个文件被读取完然后开始读取第二个文件当第二文件在被读取的时候CPU会去处理第一个文件记住在等待磁盘读取文件的时候CPU大部分时间是空闲的 总的说来CPU能够在等待IO的时候做一些其他的事情这个不一定就是磁盘IO它也可以是网络的IO或者用户输入通常情况下网络和磁盘的IO比CPU和内存的IO慢的多 程序设计更简单 在单线程应用程序中如果你想编写程序手动处理上面所提到的读取和处理的顺序你必须记录每个文件读取和处理的状态相反你可以启动两个线程每 个线程处理一个文件的读取和操作线程会在等待磁盘读取文件的过程中被阻塞在等待的时候其他的线程能够使用CPU去处理已经读取完的文件其结果就 是磁盘总是在繁忙地读取不同的文件到内存中这会带来磁盘和CPU利用率的提升而且每个线程只需要记录一个文件因此这种方式也很容易编程实现 程序响应更快 将一个单线程应用程序变成多线程应用程序的另一个常见的目的是实现一个响应更快的应用程序设想一个服务器应用它在某一个端口监听进来的请求当一个请求到来时它去处理这个请求然后再返回去监听 服务器的流程如下所述 while(serveris active){ listen for request process request }如果一个请求需要占用大量的时间来处理在这段时间内新的客户端就无法发送请求给服务端只有服务器在监听的时候请求才能被接收另一种设计是监听线 程把请求传递给工作者线程(worker thread)然后立刻返回去监听而工作者线程则能够处理这个请求并发送一个回复给客户端这种设计如下所述 while(server is active){ listen for request hand request to worker thread }这种方式服务端线程迅速地返回去监听因此更多的客户端能够发送请求给服务端这个服务也变得响应更快 桌面应用也是同样如此如果你点击一个按钮开始运行一个耗时的任务这个线程既要执行任务又要更新窗口和按钮那么在任务执行的过程中这个应用程 序看起来好像没有反应一样相反任务可以传递给工作者线程(word thread)当工作者线程在繁忙地处理任务的时候窗口线程可以自由地响应其他用户的请求当工作者线程完成任务的时候它发送信号给窗口线程窗口 线程便可以更新应用程序窗口并显示任务的结果对用户而言这种具有工作者线程设计的程序显得响应速度更快 从一个单线程的应用到一个多线程的应用并不仅仅带来好处它也会有一些代价不要仅仅为了使用多线程而使用多线程而应该明确在使用多线程时能多来的好处比所付出的代价大的时候才使用多线程如果存在疑问应该尝试测量一下应用程序的性能和响应能力而不只是猜测 设计更复杂 虽然有一些多线程应用程序比单线程的应用程序要简单但其他的一般都更复杂在多线程访问共享数据的时候这部分代码需要特别的注意线程之间的交互往往非常复杂不正确的线程同步产生的错误非常难以被发现并且重现以修复 上下文切换的开销 当CPU从执行一个线程切换到执行另外一个线程的时候它需要先存储当前线程的本地的数据程序指针等然后载入另一个线程的本地数据程序指针 等最后才开始执行这种切换称为上下文切换(context switch)CPU会在一个上下文中执行一个线程然后切换到另外一个上下文中执行另外一个线程 上下文切换并不廉价如果没有必要应该减少上下文切换的发生 增加资源消耗 线程在运行的时候需要从计算机里面得到一些资源除了CPU线程还需要一些内存来维持它本地的堆栈它也需要占用操作系统中一些资源来管理线程 我们可以尝试编写一个程序让它创建个线程这些线程什么事情都不做只是在等待然后看看这个程序在运行的时候占用了多少内存 |