一 操作系统发展史介绍
参考链接:http://www.cnblogs.com/alex3714/articles/5230609.html
二 进程与线程
进程: 对各种资源管理的集合 就可以称为进程。进程要以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等。比如qq就可以称为一个进程。进程 要操作cpu , 必须要先创建一个线程。
线程: 是操作系统最小的调度单位, 是一串指令的集合。
进程与线程的区别?
1.线程共享内存空间,进程的内存是独立的。
2.线程可以直接访问其进程的数据段,进程拥有自己父进程数据段的副本。
3.同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现
4.创建新线程很简单,创建新进程需要对其父进程进行一次克隆
5.一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程
6.对主线程的更改(取消,优先级更改等)可能会影响进程的其他线程的行为,对父进程的更改不会影响子进程。
参考链接:http://www.cnblogs.com/alex3714/articles/5230609.html
三 线程语法
我们知道,IO操作不占用cpu,计算占用cpu。
python多线程不适合cpu密集操作型的任务,适合io操作密集型的任务。
线程有2种调用方式,如下:
直接调用
import threading
import time
def runtask(n):
print("task",n)
time.sleep(2)
# 普通调用,程序执行完需要4秒
# run("t1")
# run("t2")
# 两个线程并发,程序执行完仅需要2秒
t1 = threading.Thread(target=runtask,args=("t1",))
t2 = threading.Thread(target=runtask,args=("t2",))
t1.start()
t2.start()
继承式调用
import threading
import time
class MyThread(threading.Thread):
def __init__(self,n):
super(MyThread,self).__init__()
self.n = n
def run(self):#注意此处函数名必须为run
print("task", self.n)
time.sleep(2)
t1 = MyThread("t1")
t2 = MyThread("t2")
t1.start()
t2.start()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import threading import time def runtask(n): print("task",n) time.sleep(2) # 使用for循环同时启动50个线程 for i in range(50): t = threading.Thread(target=runtask,args=("t%s" %i,)) t.start()
四 join
一个程序可以看做是一个主线程,当它启动子线程后,就不再管子线程了,此时子线程与主线程是并行的关系,同时进行。所以在主线程中无法测试子线程的运行速度,如下代码实例:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import threading import time def runtask(n): print("task",n) time.sleep(2) start_time = time.time() t1 = threading.Thread(target=runtask,args=("t1",)) t2 = threading.Thread(target=runtask,args=("t2",)) t1.start() t2.start() print(time.time()-start_time) # 输出:0.0
那么如果我一定要测试所有子线程运行的总时间怎么办呢?可以通过t.join()设置在主线程中等待子线程执行完成。join()的作用就是使子线程执行完毕后,主线程才继续往下走。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import threading import time def runtask(n): print("task",n) time.sleep(2) start_time = time.time() t1 = threading.Thread(target=runtask,args=("t1",)) t2 = threading.Thread(target=runtask,args=("t2",)) t1.start() t2.start() t1.join() #等待t1执行完成 t2.join() #等待t2执行完成 print(time.time()-start_time) # 输出:2.0011143684387207
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import threading import time def runtask(n): print("task",n) time.sleep(2) t_objs = [] #存线程实例 start_time = time.time() for i in range(50): t = threading.Thread(target=runtask,args=("t%s" %i,)) t.start() t_objs.append(t) #为了不阻塞后面线程的启动,不在这里join,先放到一个列表里 for t in t_objs: # 循环线程实例列表,等待所有线程执行完毕 t.join() print(time.time()-start_time) # 输出:2.006114959716797
注意:一定要把所有线程都启动后,才能执行t.join()等待。如果启动完一个线程就等待,然后才启动下一个线程,那么2个线程就变成了串行,完全失去了并发的功能。举例:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import threading import time def runtask(n): print("task",n) time.sleep(2) start_time = time.time() t1 = threading.Thread(target=runtask,args=("t1",)) t2 = threading.Thread(target=runtask,args=("t2",)) t1.start() t1.join() t2.start() t2.join() print(time.time()-start_time) # 输出:4.0002288818359375
如下示例,通过threading.active_count()可以查看当前活跃的线程个数,虽然启动了2个子线程,但其实一共有3个线程在运行。通过threading.current_thread()可以看出谁是主线程,谁是子线程。第一个启动的线程并不是主线程,主线程就是程序本身。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import threading import time def runtask(n): print("task",n) time.sleep(2) print(threading.current_thread()) t1 = threading.Thread(target=runtask,args=("t1",)) t2 = threading.Thread(target=runtask,args=("t2",)) t1.start() t2.start() print(threading.current_thread()) print(threading.active_count()) ####输出: ####task t1 ####task t2 ####<_MainThread(MainThread, started 8504)> ####3 ####<Thread(Thread-1, started 9996)> ####<Thread(Thread-2, started 9976)>
五 守护线程
守护线程:一个主线程可以发起多个守护线程,这些守护线程都是为主线程服务的,一旦主线程挂了,守护线程随之挂掉。守护线程的用途有帮助主线程管理资源、打开文件、监听端口、监听一些新的链接、把垃圾资源回收等,它可以干很多事情,由主线程定义。
子线程和守护线程的区别:
虽然在启动子线程后,主线程和子线程是同时运行,但是当主线程运行完后,它还是会等待所有子线程都运行完毕,程序才会退出。如下示例,可以明显感觉到主线程运行完后,程序又等待了2秒才退出。
可以在子线程start之前,通过t.setDaemon(True)将子线程设置为守护线程。此时再运行程序,你就会发现,主线程运行完成后,程序直接退出,没有等待子线程。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import threading import time def runtask(n): print("task",n) time.sleep(2) for i in range(50): t = threading.Thread(target=runtask,args=("t%s" %i,)) ####将main线程设置为Daemon线程,它做为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其它子线程会同时退出,不管是否执行完任务 # t.setDaemon(True) #把当前线程设置为守护线程, t.start() print("main thread finished")
所以,主线程一旦创建了子线程,子线程就完全独立了,它的运行不会再受主线程的控制。而守护线程不同,它的运行依赖于主线程,一旦主线程执行完毕,守护线程随之终止。
守护线程的用途:比如说现在写一个socketserver,每有一个用户连接,你就为其分配一个子线程,那么当你希望手动宕掉socketserver时,肯定不希望它等每个子线程都断完成后,才宕机,你肯定希望它一下子就宕机,不管子线程。那么此时你就可以把这些子线程设置为守护线程。