Python3学习之路~9.2 操作系统发展史介绍、进程与线程区别、线程语法、join、守护线程

时间:2021-06-28 14:55:07

一 操作系统发展史介绍

参考链接: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()
import threading
import time def runtask(n):
print("task",n)
time.sleep() # 使用for循环同时启动50个线程
for i in range():
t = threading.Thread(target=runtask,args=("t%s" %i,))
t.start()

使用for循环同时启动50个线程

四 join

一个程序可以看做是一个主线程,当它启动子线程后,就不再管子线程了,此时子线程与主线程是并行的关系,同时进行。所以在主线程中无法测试子线程的运行速度,如下代码实例:

import threading
import time
def runtask(n):
print("task",n)
time.sleep() 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()的作用就是使子线程执行完毕后,主线程才继续往下走。

import threading
import time
def runtask(n):
print("task",n)
time.sleep() 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

计算2个线程执行完所需的时间

import threading
import time def runtask(n):
print("task",n)
time.sleep() t_objs = [] #存线程实例
start_time = time.time() for i in range():
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

计算50个线程执行完毕所需的时间

注意:一定要把所有线程都启动后,才能执行t.join()等待。如果启动完一个线程就等待,然后才启动下一个线程,那么2个线程就变成了串行,完全失去了并发的功能。举例:

import threading
import time
def runtask(n):
print("task",n)
time.sleep() 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()可以看出谁是主线程,谁是子线程。第一个启动的线程并不是主线程,主线程就是程序本身。

import threading
import time
def runtask(n):
print("task",n)
time.sleep()
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 )>
####
####<Thread(Thread-, started )>
####<Thread(Thread-, started )>

五 守护线程

守护线程:一个主线程可以发起多个守护线程,这些守护线程都是为主线程服务的,一旦主线程挂了,守护线程随之挂掉。守护线程的用途有帮助主线程管理资源、打开文件、监听端口、监听一些新的链接、把垃圾资源回收等,它可以干很多事情,由主线程定义。

子线程和守护线程的区别

虽然在启动子线程后,主线程和子线程是同时运行,但是当主线程运行完后,它还是会等待所有子线程都运行完毕,程序才会退出。如下示例,可以明显感觉到主线程运行完后,程序又等待了2秒才退出。
可以在子线程start之前,通过t.setDaemon(True)将子线程设置为守护线程。此时再运行程序,你就会发现,主线程运行完成后,程序直接退出,没有等待子线程。

import threading
import time def runtask(n):
print("task",n)
time.sleep() for i in range():
t = threading.Thread(target=runtask,args=("t%s" %i,))
####将main线程设置为Daemon线程,它做为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其它子线程会同时退出,不管是否执行完任务
# t.setDaemon(True) #把当前线程设置为守护线程,
t.start() print("main thread finished")

守护线程

所以,主线程一旦创建了子线程,子线程就完全独立了,它的运行不会再受主线程的控制。而守护线程不同,它的运行依赖于主线程,一旦主线程执行完毕,守护线程随之终止。

守护线程的用途:比如说现在写一个socketserver,每有一个用户连接,你就为其分配一个子线程,那么当你希望手动宕掉socketserver时,肯定不希望它等每个子线程都断完成后,才宕机,你肯定希望它一下子就宕机,不管子线程。那么此时你就可以把这些子线程设置为守护线程。