操作系统
概念
操作系统位于计算机硬件与应用软件之间,本质也是一个软件。操作系统由操作系统的内核(运行于内核态,管理硬件资源)以及系统调用(运行于用户态,为应用程序员写的应用程序提供系统调用接口)两部分组成。
操作系统可以管理硬件设备,并将对硬件的管理封装成系统调用,并为用户和应用程序提供一个简单的接口,以便于使用
发展史
第一代计算机(1940-1955)
真空管和穿孔卡片
没有操作系统的概念,所有的程序设计都是直接操控硬件
优点:程序员在申请的时间段内独享整个资源,可以即时地调试自己的程序(有bug可以立刻处理)
缺点:浪费计算机资源,一个时间段内只有一个人用。
第二代计算机(1955-1965)
晶体管和批处理系统
优点:批处理,节省了机时
缺点:
1.整个流程需要人参与控制,将磁带搬来搬去(中间俩小人)
2.计算的过程仍然是顺序计算-》串行
3.程序员原来独享一段时间的计算机,现在必须被统一规划到一批作业中,等待结果和重新调试的过程都需要等同批次的其他程序都运作完才可以(这极大的影响了程序的开发效率,无法及时调试程序
第三代计算机(1965-1980)
集成电路芯片和多道程序设计
开发出SPOOLING技术:不必将磁带搬来搬去了(中间俩小人失业了),强化了操作系统的功能
多道程序设计:
空间上的复用:将内存分为几部分,每个部分放入一个程序,这样,同一时间内存中就有了多道程序。
时间上的复用:当一个程序在等待I/O时,另一个程序可以使用cpu,如果内存中可以同时存放足够多的作业,则cpu的利用率可以接近100%,类似于我们小学数学所学的统筹方法。
(操作系统采用了多道技术后,可以控制进程的切换,或者说进程之间去争抢cpu的执行权限。这种切换不仅会在一个进程遇到io时进行,一个进程占用cpu时间过长也会切换,或者说被操作系统夺走 cpu的执行权限)
第四代计算机(1980~至今):个人计算机
进程和线程
进程
程序仅仅只是一堆代码而已,而进程指的是程序的运行过程(最小的资源单位,是抽象的概念)。
同一个程序执行两次,那也是两个进程
进程一般由程序、数据集、进程控制块三部分组成。
线程
线程是最小的执行单位
进程和线程的关系
1 一个程序至少有一个进程,一个进程至少有一个线程.(进程可以理解成线程的容器) 2 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 3 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和 4 程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 5 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调 6 度的一个独立单位. 7 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程 8 自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是 9 它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 10 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
python的GIL
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
翻译:无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行
一个进程中的多个线程同一时刻只能由一个cpu执行其中的一个线程,导致多核没有利用上,可以使用进程+协程来解决
对于IO密集型的任务,python的多线程是有意义的
对于计算密集型的任务,python的多线程就不推荐使用的,可以使用
可以使用多进程+协程达到多线程的效果
time.sleep()等同于IO操作
多线程与 threading模块
hreading 模块建立在thread 模块之上。thread模块以低级、原始的方式来处理和控制线程,而threading 模块通过对thread进行二次封装,提供了更方便的api来处理线程。
线程调用
直接调用
import threading import time def sayhi(num): #定义每个线程要运行的函数 print("running on number:%s" %num) time.sleep(3) if __name__ == '__main__': t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例 t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例 t1.start() #启动线程 t2.start() #启动另一个线程 print(t1.getName()) #获取线程名 print(t2.getName())
继承式调用
t1.start()会默认调用t1.run()方法
以下函数共运行5s,若不使用线程则需要8s
import threading import time class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self):#定义每个线程要运行的函数 print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start() print("ending......")
hreading.thread的实例方法
join&Daemon方法
t1.join() 在子线程t1完成运行之前,这个子线程的父线程将一直被阻塞

1 import threading,time 2 3 def eat(money): #t1 4 print(time.ctime(), ' start to eat, need %s¥' %(money)) 5 time.sleep(3) 6 print(time.ctime(), ' eating..') 7 time.sleep(3) 8 print(time.ctime(), ' end to eat agin') 9 10 def drink(money): #t2 11 print(time.ctime(), ' start to drink, need %s¥' %(money)) 12 time.sleep(5) 13 print(time.ctime(), ' end to drink') 14 15 16 if __name__ == '__main__': 17 t1 = threading.Thread(target=eat, args=(15,)) #创建线程对象 18 # print(type(t1)) 19 t2 = threading.Thread(target=drink, args=(1,)) 20 21 # t1.start() #将t1加入主线程,t1不用cpu时就暂时保存t1的运行状态,继续往下执行t2,待t1需要cpu时再次执行 22 # t2.start() 23 24 t1.start() #将t1加入主线程,t1不用cpu时就暂时保存t1的运行状态,继续往下执行 25 t1.join() #继续执行t1至t1结束 26 print('这是有关线程的一些演示01') 27 t2.start() 28 print('这是有关线程的一些演示02') 29 30 print('t1是否是活动的',t1.isAlive()) 31 print('t1的线程名', t1.getName())
守护线程,在调用前设置.守护线程用来守护主线程,你有我有,你没我没.设置线程为守护线程,‘不重要’,主线程结束时该线程会随之结束

import threading from time import ctime,sleep import time def ListenMusic(name): print ("Begin listening to %s. %s" %(name,ctime())) sleep(3) print("end listening %s"%ctime()) def RecordBlog(title): print ("Begin recording the %s! %s" %(title,ctime())) sleep(5) print('end recording %s'%ctime()) threads = [] t1 = threading.Thread(target=ListenMusic,args=('水手',)) t2 = threading.Thread(target=RecordBlog,args=('python线程',)) threads.append(t1) threads.append(t2) if __name__ == '__main__': # t1.setDaemon(True) # t2.setDaemon(True) for t in threads: #t.setDaemon(True) #注意:一定在start之前设置 t.start() # t.join() # t1.join() # t1.setDaemon(True) # t2.join()########考虑这三种join位置下的结果? print ("all over %s" %ctime())
其他线程实例的方法
# run(): 线程被cpu调度后自动执行线程对象的run方法 # start():启动线程活动。 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
同步锁
多个线程都在同时操作同一个共享资源,所以造成了资源破坏,怎么办呢?(join会造成串行,失去所线程的意义)

1 import time 2 import threading 3 4 def addNum(): 5 global num #在每个线程中都获取这个全局变量 6 #num-=1 7 8 temp=num 9 #print('--get num:',num ) 10 time.sleep(0.1) 11 num =temp-1 #对此公共变量进行-1操作 12 13 num = 100 #设定一个共享变量 14 thread_list = [] 15 for i in range(100): 16 t = threading.Thread(target=addNum) 17 t.start() 18 thread_list.append(t) 19 20 for t in thread_list: #等待所有线程执行完毕 21 t.join() 22 23 print('final num:', num )
使用同步锁解决,位于锁中的代码被执行完才能切换线程
1 R=threading.Lock() #获取锁的对象 2 3 #### 4 def sub(): 5 global num 6 R.acquire() #获得一把锁 7 temp=num-1 8 time.sleep(0.1) 9 num=temp 10 R.release() #释放锁
递归锁
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。下面是一个死锁的例子:

1 import threading,time 2 3 class myThread(threading.Thread): 4 def funA(self): 5 lock_A.acquire() 6 print(self.name, 'get lock_A', time.ctime()) 7 time.sleep(2) 8 lock_B.acquire() 9 print(self.name, 'get lock_B', time.ctime()) 10 time.sleep(2) 11 lock_B.release() 12 lock_A.release() 13 14 def funB(self): 15 lock_B.acquire() 16 print(self.name, 'get lock_B', time.ctime()) 17 time.sleep(2) 18 lock_A.acquire() 19 print(self.name, 'get lock_A', time.ctime()) 20 time.sleep(2) 21 lock_A.release() 22 lock_B.release() 23 24 def run(self): 25 self.funA() 26 self.funB() 27 28 th_li = [] 29 lock_A = threading.Lock() 30 lock_B = threading.Lock() 31 32 if __name__ == '__main__': 33 for i in range(4): 34 th_li.append(myThread()) 35 for t in th_li: 36 t.start() 37 # 此时会产生死锁,一个线程得到了锁B,并等待锁A的释放,另一个线程得到了锁A,并等待锁B的释放, 38 # 这个时候双方都不会主动释放,造成死锁 39 # for t in th_li: 40 # t.join()
lock=threading.RLock()
为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

1 # 递归锁 2 import threading,time 3 4 class myThread(threading.Thread): 5 def funA(self): 6 lock.acquire() 7 print(self.name, 'get lock_A', time.ctime()) 8 time.sleep(1) 9 lock.acquire() 10 print(self.name, 'get lock_B', time.ctime()) 11 time.sleep(1) 12 lock.release() 13 lock.release() 14 15 def funB(self): 16 lock.acquire() 17 print(self.name, 'get lock_B', time.ctime()) 18 time.sleep(1) 19 lock.acquire() 20 print(self.name, 'get lock_A', time.ctime()) 21 time.sleep(1) 22 lock.release() 23 lock.release() 24 25 def run(self): 26 self.funA() 27 self.funB() 28 29 30 lock = threading.RLock() #递归锁 31 th_li = [] 32 if __name__ == '__main__': 33 for i in range(4): 34 th_li.append(myThread()) 35 for t in th_li: 36 t.start()
同步条件(Event)
利用Event可以设置进程A必须等待进程B的某部分代码执行结束才能继续执行A
An event is a simple synchronization object;the event represents an internal flag,
and threads can wait for the flag to be set, or set or clear the flag themselves.
event = threading.Event()
# a client thread can wait for the flag to be set
event.wait()
# a server thread can set or reset it
event.set()
event.clear()
If the flag is set, the wait method doesn’t do anything.
If the flag is cleared, wait will block until it becomes set again.
Any number of threads may wait for the same event.

import time import threading class Account: def __init__(self, _id, balance): self.id = _id self.balance = balance self.lock = threading.RLock() def withdraw(self, amount): with self.lock: self.balance -= amount def deposit(self, amount): with self.lock: self.balance += amount def drawcash(self, amount):#lock.acquire中嵌套lock.acquire的场景 with self.lock: interest=0.05 count=amount+amount*interest self.withdraw(count) def transfer(_from, to, amount): #锁不可以加在这里 因为其他的其它线程执行的其它方法在不加锁的情况下数据同样是不安全的 _from.withdraw(amount) to.deposit(amount) alex = Account('alex',1000) yuan = Account('yuan',1000) t1=threading.Thread(target = transfer, args = (alex,yuan, 100)) t1.start() t2=threading.Thread(target = transfer, args = (yuan,alex, 200)) t2.start() t1.join() t2.join() print('>>>',alex.balance) print('>>>',yuan.balance)
信号量(Semaphore)
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)
BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

import threading,time class myThread(threading.Thread): def run(self): if semaphore.acquire(): print(self.name) time.sleep(5) semaphore.release() if __name__=="__main__": semaphore=threading.Semaphore(5) #设置线程并发数为5 thrs=[] for i in range(100): thrs.append(myThread()) for t in thrs: t.start()
多线程与队列Queue

1 import threading,time 2 3 li=[1,2,3,4,5] 4 5 def pri(): 6 while li: 7 a=li[-1] 8 print(a) 9 time.sleep(1) 10 try: 11 li.remove(a) 12 except Exception as e: 13 print('----',a,e) 14 15 t1=threading.Thread(target=pri,args=()) 16 t1.start() 17 t2=threading.Thread(target=pri,args=()) 18 t2.start()
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
1 创建一个“队列”对象 2 import Queue 3 q = Queue.Queue(maxsize = 10) 4 Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。 5 6 将一个值放入队列中 7 q.put(10) 8 调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为 9 1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。 10 11 将一个值从队列中取出 12 q.get() 13 调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True, 14 get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。 15 16 Python Queue模块有三种队列及构造函数: 17 1、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize) 18 2、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize) 19 3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize) 20 21 此包中的常用方法(q = Queue.Queue()): 22 q.qsize() 返回队列的大小 23 q.empty() 如果队列为空,返回True,反之False 24 q.full() 如果队列满了,返回True,反之False 25 q.full 与 maxsize 大小对应 26 q.get([block[, timeout]]) 获取队列,timeout等待时间 27 q.get_nowait() 相当q.get(False) 28 非阻塞 q.put(item) 写入队列,timeout等待时间 29 q.put_nowait(item) 相当q.put(item, False) 30 q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 31 q.join() 实际上意味着等到队列为空,再执行别的操作
import queue #先进后出 q=queue.LifoQueue() q.put(34) q.put(56) q.put(12) #优先级 # q=queue.PriorityQueue() # q.put([5,100]) # q.put([7,200]) # q.put([3,"hello"]) # q.put([4,{"name":"alex"}]) while 1: data=q.get() print(data)

1 import time,random 2 import queue,threading 3 4 q = queue.Queue() 5 6 def Producer(name): 7 count = 0 8 while count <10: 9 print("making........") 10 time.sleep(random.randrange(3)) 11 q.put(count) 12 print('Producer %s has produced %s baozi..' %(name, count)) 13 count +=1 14 #q.task_done() 15 #q.join() 16 print("ok......") 17 def Consumer(name): 18 count = 0 19 while count <10: 20 time.sleep(random.randrange(4)) 21 if not q.empty(): 22 data = q.get() 23 #q.task_done() 24 #q.join() 25 print(data) 26 print('