进程与线程
参考学习方法:
1.通读博客
2.看笔记,照着上课的代码重新敲一遍
3.在敲代码的过程中再确认一下是不是有不了解的地方
4.如果有不了解的 : 和周围的同学一起讨论 找周围学习好的同学问 来问老师
5.一遍敲笔记 一遍把注释写下来
6.把你敲得代码删掉,只留注释
7.不要看笔记,对照注释默写代码
操作系统的作用:
1:隐藏丑陋的硬件端口,提供;良好的抽象接口给用户更好的体验
2:管理,调度进程,并将对进程的竞争变得有序
多道技术:
针对单
核,实现并发
3解决多个代码在执行时,抢占资源,对多个进程的切换去运行
1;时间上的复用:
在同一时间完成
a.cpu遇到io阻塞切换至其他进程
b,cpu处理一个进程时间过长就会切换
2:空间复用:多个进程占有内存,
3.在任务之间的切换时根据io 阻塞来切换的.提高了效率
进程在操作系统的三状态:
就绪 阻塞 运行
(1)就绪(Ready)状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
(2)执行/运行(Running)状态当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。
(3)阻塞(Blocked)状态正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、
申请缓冲区不能满足、等待信件(信号)等。
分时操作系统:
操作系统会根据cpu使用的时间来切换,
时间片:每个人任务分配cpu 执行的时间
同时执行,每个进程都可以占用cpu 执行,
能够把几个任务之间的数据隔离开,并且记录执行中的数据状态
更好的提供了用户体验
串行 和 并行
# 并发 : 站在多个程序/任务的角度上看的,来回切换去执行,
# 并行 :多个CPU,多个任务同时在CPU上执行
# 串行 :一个CPU,代码在这个cpu上一条一条顺序执行
# 站在CPU执行指令的角度上看的
同步异步
# 同步 : 执行一个任务,必须要等待这个任务有结果才返回/才进行下一个任务
# sk.accept
# conn.recv
# conn.send
# login
# 异步 : 执行一个任务,不必等待这个任务的结果
阻塞非阻塞 : 看cpu有没有停止对这个任务的执行
# 阻塞 让你的程序会陷入等待的 recv recvfrom accept sleep input acquire
# 非阻塞
同步阻塞\同步非阻塞\异步阻塞\异步非阻塞
同步阻塞:
程序必有回应,才能继续接着运行如((accept,recv 然后接收的那一端就不会做然后事情)
同步非阻塞: 在同步运行的时候,不阻塞可以运行其它的进程
异步非阻塞:俩者异步的,并且不阻塞,进程继续执行
阻塞:
阻塞调用是指调用结果返回结果之前,当前线程会被挂起的,函数只有在得到结果后才会将阻塞的线程激活
,而同步调的当前线程是激活的,只是逻辑上当前函数没有返回罢了
1. 同步调用:apply一个累计1亿次的任务,该调用会一直等待,直到任务返回结果为止,但并未阻塞住(即便是被抢走cpu的执行权限,那也是处于就绪态);
2. 阻塞调用:当socket工作在阻塞模式的时候,如果没有数据的情况下调用recv函数,则当前线程就会被挂起,直到有数据为止。
进程与线程区别:
多进程
多个程序 能够 同时在操作系统中 执行
多进程 : 开销 数据隔离
一个程序 不太适合开启很多个进程
并发的需求越来越大,多个CPU的出现
一个新的单位
线程
线程是进程的一部分
一个线程必须依赖进程存在
一个进程之内至少有一条线程
进程中默认存在的线程 主线程
也可以在一个进程中开启多个子线程来完成更多的事情
线程 - 轻量级(轻型)进程
比进程的开销小很多
进程和线程之间的关系
线程是属于进程
进程是计算机中最小的 资源分配单位
线程是计算机中能被CPU调度的最小单位,计算机中最小的执行单位
多个进程之间的多条线程可不可以利用多核
同一个进程中的多条线程可不可以利用多核
一个进程之间的多个线程是完全可以利用多核的
正常的进程和线程之间的区别
进程 : 数据隔离\开销大
线程 : 数据共享\开销小
**********进程:
操作系统引入进程的概念的原因:
从理论上来说,是对正在运行的程序过程的抽象
从实现的角度 来说,是一种数据结构,目的在于清晰的刻画出动画系统的内在规律
有效管理合和调度进入计算机系统主内存储存器运行的程序
进程的概念:
进程:就是计算机关于数据集合的一次活动,是系统进行资源分配基本单位,是操作系统结构的基础,
进程是一个实体 有内存空间,包括文件区域,数据区域,文本区域执行处理机制的代码,
堆栈区域储存着活动过程调用的指令和本地变量
也可以说进程是:
1.运行中的程序就是一个进程。所有的进程都是通过它的父进程来创建的。因此,运行起来的python程序也是一个进程,
那么我们也可以在程序中再创建进程。多个进程可以实现并发效果,也就是说,当我们的程序中存在多个进程的时候,
在某些时候,就会让程序的执行速度变快。
2.进程是多道技术出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序活动的规律引出来的一个概练,
所有多道程序的设计操作系统都是建立在进程的基础上.
进程的特点:
动态性;进程的实质就是多道程序系统中的一次执行,进程是动态产生,动态的消亡
并发性:任何进程都可以同其他的进程一起并发执行
独立性:由于进程是一个独立的运行的基本单位,同时也是系统分配资源和调用的独立单位
异步性:由于进程间是互相制约的,使用进程具有执行 的时间间断性
结构特性:进程由程序,数据和进程控制 这三块组成的
多个不同的进程可以包含相同的程序,一个程序在不同的数据里构成不同的进程,能够得到不同的结果,
但是执行过程中,程序不能发生改变
程序可以作为一种软件质料长期存储,而进程是具有一定的生命期
并行:同时多个进程进行执行 多核的系统
并发:并发资源有限情况下,是来回切这着进行 单核的必定存在
同步:
一个结束了,才能进行下一个程序
异步:给一个程序发送指令,就不用管了就自己运行自己的
同步阻塞:
被等待了,一值在等待着:拿函数来说,该程序一直阻塞在该函数的调用处,就不能继续往下执行
异步阻塞:在被通知的消息被触发时,就被阻塞在这个等待操作上了
linux:
理论上: 各个进程的开启和结束互相不影响,不依赖.
主进程的开启与子进程开启没有任何依赖.
什么叫进程结束?
代码运行完毕叫进程结束么? 你的进程空间在内存中真正的消失才算是进程结束.
子进程是在主进程的基础上创建的,并且结束子进程的时间节点主进程必须要知道.
僵尸进程: 死而不腐, p1 p2 p3 在代码结束时都没有完全死掉,
他们会形成一个僵尸进程:僵尸进程只会保存pid,运行时间,状态.
我的主进程为什么要获取僵尸进程的这些保留的数据呢?
僵尸进程会占有一点空间,僵尸进程会占有pid进程号,
僵尸进程的回收,是由主进程发起的.
孤儿进程?
你的主进程意外挂了,剩下 p1 p2 p3 就形成 孤儿进程
所以你的孤儿都会交给'民政局'处理. init 是 Linux 所有进程的主进程.
僵尸进程有害?有害的.
僵尸进程他的回收取决于主进程,如果主进程产生了大量的子进程,但是不着急回收这些变成了的僵尸进程.
孤儿进程有害? 无害 都会被民政局 init 回收.
具体回收方法:windos 有 waitpid()
p1.join()源码中 有waitpid()方法.
在python程序中的进程操作
multiprocessing 模块,分为四个模块:
1创建进程部分Process() 2进程通信的部分 Queue()
3进程池的部分 Pool() 4进程之间的数据共享部分 Mannage
在Linux 与windos 系统有区别的:
在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制)
,在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()
直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’
判断保护起来,import 的时候 ,就不会递归运行了。
进程之间的通信:IPC(各种队列)
列如进程中的队列:
本类进程之间 的信息是独立的,互不相关,所以想要实现进程之间的通信就得,用到队列 先进先出,put get
同样的是通过导入模块来使用
最重要的特点就是先进先出
进程锁:
# 抢票的例子
直接锁一个函数,或者一代码都可以
那段函数,功能使用到数据了 为了 数据安全就得给它加上锁.
with lock :
func()
因为本身抢票 ,就是一个多并发的,因为数据在操作的过程中是需要时间,网络上的阻塞的,不能并发,只能串行
进程队列
队列的特点 : 先进先出 进程之间数据安全
就绪队列 : 由操作系统维护这任务之间谁先被运行的顺序的一个数据结构
队列的底层 : 管道 + 锁
# 管道Pipe 是没有锁的 数据不安全的
# from multiprocessing import Pipe
管道的底层 :
socket 基于文件地址家族的通信
进程池:Pool
1 .为什么要有进程池 ?
在创建进程是需要耗费时间,销毁也需要时间,也就减低了效率
2 .多个进程能不能利用多核(多个cpu)
能最多能利用多少个cpu?电脑有多少个cpu就能够最多同时利用多少个进程
如果我们可以忽略IO操作,这个时候我们起超过cpu个数过多的进程个数反而会降低程序的执行效率
进程池:
概念:对多个任务,只创建对应的几个线程,而不是每一个任务都去创建一个线程:
异步提交的时候,当几个任务很短暂的时候,就会被第一个进程给执行完了,所以他们的进程id是一样的
进程池中就没有必要在去创造第二条进程了
一般用于高级算型的
同步提交基本不用,一个一个进程去跑的,就会起多个进程
在进程池中获取返回值 因为进程之间数据本身就是隔离的
Lock 是从魔块中到入的,把一段代码变为串行,把异步变为同步
所以加锁保证数据安全
查票可以并发但是买票就不能了 因为在并发中,cpu切换得很快 而不是
等一个程序结束了在进行另外一个程序,在写入json 是还写好已经被读了所以就导致一张票多卖
线程:
为什么会有线程
socket 网络上的服务
我们希望我们的应用能够在同一时刻做更多的事情
并且还能共享一段数据
线程:
概念:是cpu调度的单位 也是轻型的进程
线程就是依赖于进程 , 在同一个进程中的线程共享进程中的数据
用法: 用于实现多并发,来回切换比多进程之间的切换还要快,在io阻塞长的话时用进程?
还是说用多进程,以为多进程是支持多核的,而多线程是不支持多核的()仅限于在python 解释器中pyCharm)
threading
Thread类
线程的效率
线程之间的数据共享
线程不同于进程的地方 :效率 数据共享 不能在外部被结束(terminate)
使用面向对象的方式开启线程
守护线程
线程的数据安全问题
GIL锁
互斥锁
递归锁
死锁现象
queue模块
能够保证数据在多个线程之间进行安全的数据传递
队列
栈
优先级队列
concurrent
线程池
进程池
线程池 :
这是一个公共的模块
进程池 线程池
都可以去用的,高度去用的
队列锁:
锁:
1:Lock 锁
2:Rlock递归锁
3:都会造成死锁的现象,当Lock锁死锁了可以用递归锁先去解决,在考利逻辑问题,把多个变量锁一起
从根本商 去解决问题
队列:
Queue 对列
fifo 先进先出
进程的代码: import os os.getpid()当前进程的id os.getppid() 父进程的id 开启进程的俩种方法: 第一种: from multiprocessing import Process import os def func(name): print(f'{name}好嗨哦') def main(): print(os.getpid()) if __name__ == '__main__': p = Process(target=func,args=(1,)) p.start() 第二种类的方法: class Myclass(Process): def __init__(self,name): super().__init__() self.name =name def run(self): # 子进程的代码都写在这 a = 1 b = 2 print('%s is runing' %self.n) if __name__ == '__main__': p = Myclass('ok') p.start() 进程池的异步提交: apply(函数, args=(参数)) 同步调用一般不用 import os from multiprocessing import Pool def func(i, ): time.sleep(0.1) i * i print(os.getpid()) if __name__ == '__main__': strat = time.time() p = Pool() 一: 异步提交 for i in range(10): p.apply_async(func, (i,)) # apply提交任务 asyncy 异步 p.close() # 关闭池子 p.join() # z阻塞 print(time.time() - strat) 二: strat = time.time() p_l = [] for i in range(20): p = Process(target=func, args=(1,)) p.start() p_l.append(p) for p in p_l: p.join() print(time.time() - strat) 进程池异步提交: import time from multiprocessing import Process, Pool def func(i): i + i return '*' * i if __name__ == '__main__': p = Pool() lis = [] # 一 a = time.time() ret = p.map(func, range(5)) for i in ret: print(i) print(time.time() - a) 二 a = time.time() for i in range(5): ret = p.apply_async(func, (i,)) lis.append(ret) for ret in lis: print(ret.get()) print(time.time() - a) 三 a = time.time() for i in range(5): ret = p.apply_async(func, (i,)) lis.append(ret) p.close() p.join() # 执行完了一起出来 for i in lis: print(i.get()) print(time.time() - a) ** ** ** map import os from multiprocessing import Pool def func(i, ): time.sleep(0.1) i * i return "i" * i if __name__ == '__main__': p = Pool() re_1 = p.map(func, range(5)) # map 内置了close 与 join for i in re_1: print(re_1) ''' 生成者消费者模型 import time,random from multiprocessing import Process,Queue def consumer(q,name): while 1: task = q.get() if not task:break time.sleep(0.1) print(f'{name}吃了{task}') def producer(q,n): for i in range(n): time.sleep(random.random()) print(f'生成了{i}个') q.put(f'生成了{i}个') if __name__ == '__main__': q = Queue() lis = [] for i in range(5): p = Process(target=producer,args=(q,10))#五个生产者 p.start() lis.append(p) p1 = Process(target=consumer,args=(q,'alex'))#俩个消费者 p2 = Process(target=consumer,args=(q,'big')) p1.start() p2.start() for p in lis: p.join() q.put(None)#为什么叫put(None) 而且因为有俩生产者,要是在生产者的子进程中put(None) 的话不够取 所以在就主进程中俩put(None) q.put(None)#应为生产的不够吃,想要结束的话就用ifp判断让消费者不在消费,多少个消费者就多少个put(None) # 进程中的队列,解决了,进程之间的通信,包括子进程与父进程的通信 #基于socket 用多进程解决并发聊天 # server 端 import socket from multiprocessing import Process server = socket.socket() server.setsockopt(socket.SOL_SOCKET,socket.SOCK_STREAM,1) server.bind(('127.0.0.1',8888)) server.listen(5) def talk(conn,client_addr): while 1: try: mag = conn.recv(1024) if not mag:break conn.send(b'adfs') except Exception: break if __name__ == '__main__': while 1: conn,addr = server.accept() p = Process(target=talk,args=(conn,addr)) p.start() # 客户端client client =socket.socket() client.connect(('127.0.0.1',8888)) while 1: client.send(b'sdfsa') msg = client.recv(1024) print(msg) #******** 线程的代码 '''下面代码为了证明:子线程与父线程的数据共享的 from threading import Thread n =10 def fucn(): global n n -=1#----->子线程n 的变化 lis = [] for i in range(10): t = Thread(target=fucn)#开启十个线程 t.start() lis.append(t) print(n) for i in lis: i.join() print(n) #-----主线程n ''' # 主线程会等待子线程结束后才结束 #查看线程名 import time from threading import Thread def func(): time.sleep(1) print(' in func') t = Thread(target=func) t.start() print(t.ident)#线程地址 print(t.name)#线程名称 类方式 查看线程名 import time class MyThread(Thread): def __init__(self,i): super().__init__() def run(self): time.sleep(1) print('in mythread',self.name,self.ident) for i in range(5): t = MyThread(i) #需要传参的话就需要,__init__ t.start() import time from threading import currentThread,active_count,enumerate,Thread def func(): time.sleep(1) #导入currentThread直接使用,直接在函数中点 得到线程名,线程地址 print('in func',currentThread().name,currentThread().ident) t = Thread(target=func) t.start() print(t.ident)#线程名 print(t.name)#线程地址 def foo(): time.sleep(1) print(' in func',currentThread().name,currentThread().ident) t =Thread(target=func) print(enumerate()) # 当前所有活着的线程对象组成的列表 print(active_count()) # 统计当前所有活着的线程对象的个数 len(enumerate) print(t.ident) # 线程id print(t.name) # 线程名 线程的守护进程 import time from threading import Thread def func(): while 1: time.sleep(1) print('in func') def son(): print('son start ') time.sleep(1) t = Thread(target=func) t.setDaemon(True) t.start() Thread(target=son,).start() time.sleep(4) 线程是否存在数据安全问题 数据共享 GIL锁 :能够保证在同一时刻 不可能有两个线程 同时执行 CPU指令 为什么有了GIL还需要手动加锁 为什么append这样的方法和+=这样的方法在数据安全性问题上是不一样 由于append是一个完整的指令过程,执行了就相当于添加,不执行就还没添加 += 先执行+法,再对结果进行赋值 线程的数据安全: from threading import Lock import dis l = [] def func(): l.append(1) a= 0 def func2(): global a with lock: a +=1 lock =Lock() dis.dis(func2()) print(a) from threading import Thread,Lock import os,time def work(): global n with lock: temp = n time.sleep(0.1) n = temp - 1 n = temp - 1 if __name__ == '__main__': n =10 l =[] for i in range(5): p = Thread(target=work,args=(lock,)) l.append(p) p.start() for p in l : p.join() print(n) #科学家吃面的问题 import time from threading import Lock,Thread noodle_lock = Lock() fork_lock = Lock() fork = 10 noodle = 20 def eat1(name): noodle_lock.acquire() print('%s拿到面了'%name) fork_lock.acquire() print('%s拿到叉子了' % name) noodel =fork + noodle time.sleep(0.1) print('%s吃到面了'%name) fork_lock.release() print('%s放下叉子了' % name) noodle_lock.release() print('%s放下到面了'%name) def eat2(name): fork_lock.acquire() print('%s拿到叉子了' % name) noodle_lock.acquire() print('%s拿到面了'%name) noodel =fork + noodle time.sleep(0.1) print('%s吃到面了'%name) noodle_lock.release() print('%s放下到面了' % name) fork_lock.release() print('%s放下叉子了' % name) # for i in ['yuan','alex','wusir','太亮']: Thread(target=eat1,args=('yuan',)).start() Thread(target=eat2,args=('alex',)).start() Thread(target=eat1,args=('wusir',)).start() Thread(target=eat2,args=('太亮',)).start() # 递归锁 # 使用递归锁可以快速的解决死锁问题 import time from threading import RLock,Thread fork_lock = noodle_lock = RLock() fork = 10 noodle = 20 def eat1(name): noodle_lock.acquire() print('%s拿到面了'%name) fork_lock.acquire() print('%s拿到叉子了' % name) noodel =fork + noodle time.sleep(0.1) print('%s吃到面了'%name) fork_lock.release() print('%s放下叉子了' % name) noodle_lock.release() print('%s放下到面了'%name) def eat2(name): fork_lock.acquire() print('%s拿到叉子了' % name) noodle_lock.acquire() print('%s拿到面了'%name) noodel =fork + noodle time.sleep(0.1) print('%s吃到面了'%name) noodle_lock.release() print('%s放下到面了' % name) fork_lock.release() print('%s放下叉子了' % name) # for i in ['yuan','alex','wusir','太亮']: Thread(target=eat1,args=('yuan',)).start() Thread(target=eat2,args=('alex',)).start() Thread(target=eat1,args=('wusir',)).start() Thread(target=eat2,args=('太亮',)).start() # 用互斥锁 来解决问题 # 两个资源作为一个资源锁起来 import time from threading import Lock,Thread lock = Lock() fork = 10 noodle = 20 def eat1(name): lock.acquire() print('%s拿到面了'%name) print('%s拿到叉子了' % name) noodel =fork + noodle time.sleep(0.1) print('%s吃到面了'%name) print('%s放下叉子了' % name) print('%s放下到面了'%name) lock.release() def eat2(name): lock.acquire() print('%s拿到叉子了' % name) print('%s拿到面了'%name) noodel =fork + noodle time.sleep(0.1) print('%s吃到面了'%name) print('%s放下到面了' % name) print('%s放下叉子了' % name) lock.release() # for i in ['yuan','alex','wusir','太亮']: Thread(target=eat1,args=('yuan',)).start() Thread(target=eat2,args=('alex',)).start() Thread(target=eat1,args=('wusir',)).start() Thread(target=eat2,args=('太亮',)).start() 1.死锁现象 由于多个锁对多个变量管理不当 产生的一种现象 2.Lock 互斥锁 在一个线程内只能被acquire一次 3.RLock 递归锁 在一个线程内可以被acquire多次,必须acquire多少次,release多少次 # 队列: # import queue # fifo 先进先出 q = queue.Queue() q.get() q.get_nowait() q.put() q.put_nowait() lifoqueue 后进先出的队列 = 栈 --> 算法 lifoq = queue.LifoQueue() lifoq.put(1) lifoq.put(3) lifoq.put(2) lifoq.put(0) print(lifoq.get()) print(lifoq.get()) 优先级队列 pq = queue.PriorityQueue()#优先级 pq.put((2,'wusir')) pq.put((1,'yuan')) pq.put((1,'alex')) pq.put((3,'太亮')) print(pq.get()) print(pq.get()) print(pq.get()) 在put()中按第一个数字的大小出来的, 出现了俩个大小一样的,元组里边的第二元素的ascll码来排 最小的先出去 可以用去写QQ会员这样的软件 线程池代码 import time from concurrent.futures import ThreadPoolExecutor as Executor ,ProcessPoolExecutor -------------------------------->ThreadPoolExecutor as Executor() 或者ProcessPoolExecutor as Executor() 这样就可以无缝去使用 def func(i): time.sleep(1) print('in son thread',i) #$****(1) if __name__ == '__main__': tp = Executor() # 线程池 tp.map(func,range(8)) #map 函数特别注意的是,参数位置,函数的位置不是关键字参数 传参的是可迭代对象 print('所有的任务都快执行完成了') #******(2) 俩种的效果是一样的 if __name__ == '__main__': tp =Executor() for i in range(5): tp.submit(func,i)#这个位置直接传参 #相当于 异步提交 tp.shutdown() #写话主线程就会等待子线程完成以后在执行主线程 相当于close 和 join print('这里跑的是主线程') #如何获取返回值的 import time,os from concurrent.futures import ThreadPoolExecutor as Executor ,ProcessPoolExecutor from threading import currentThread def func(i): i + i time.sleep(1) print(os.getpid(),os.getppid()) return '^'*i if __name__ == '__main__': p = Executor(4) 第一种使用使用map提交任务 获取返回值 ret = p.map(func,range(3))#ret generator object 生成对象 for i in ret: print(i) print('这是主线程---->',os.getpid(),os.getppid()) 第二种使用submit提交任务,获取返回值 l =[] for i in range(5): ret = p.submit(func,i) l.append(ret) for i in l: print(i.result()) def func(i): i + i time.sleep(1) print(currentThread().name,currentThread().ident) if __name__ == '__main__': p = Executor() p.map(func,range(6)) print('这是主线程------>',currentThread().name,currentThread().ident) # # #回调函数: import time,os,random,requests from concurrent.futures import ThreadPoolExecutor as Executor ,ProcessPoolExecutor from threading import currentThread,Thread def back(ret): print(ret.result()) def son_func(i): time.sleep(random.random()) return i**i p = Executor() for i in range(5): ret =p.submit(son_func,i)#得到函数son_func返回值 ret.add_done_callback(back)#返回值传个back def get(url): obj = requests.get(url) if requests.status_codes == 200: return {'url':url,'text':obj.text} def parse_page(res): res = res.result() parse_res = 'url:<%s> size:[%s]\n' % (res['url'], len(res['text'])) with open('db.txt','a') as f : f.write(parse_res) if __name__ == '__main__': urls =[ 'https://www.baidu.com', 'https://www.python.org', ] p = Executor() for url in urls: p.submit(get).add_done_callback(parse_page) #二分法查找 # lis =[1,2,13,14,25,61,73,85,97,123,345,678,1234,5678,6789] #第一种纯逻辑算法: n =345 lift = 0 right = len(lis) - 1 count = 0 while lift<=right: middle = (lift + right) // 2 if n > lis[middle]: lift = middle +1 elif n < lis[middle] : right = middle - 1 else: print(count) print(middle) break count +=1 else: print('不存在') # #第二种: lis =[1,2,13,14,25,61,73,85,97,123,345,678,1234,5678,6789] def func(left,right,n): middle = (left + right) // 2 if left>right: #----->边界问题 当数不存在的时候就找不到,left 与right 下标都变互逆了 print('没找到') return -1 if n > lis[middle]: left = middle + 1 elif n < lis[middle]: right = middle -1 else: return middle return func(left,right,n) p = func(0,len(lis) - 1,1) print(p) # 第三种:改变列表 lis =[1,2] left = 0 right = len(lis) - 1 middle = (left + right) // 2 lis=lis[:middle-1] print(lis) lis =[1,2,13,14,25,61,73,85,97,123,345,678,1234,5678,6789] def func(lis,n): left = 0 right = len(lis) - 1 middle = (left + right) // 2 if right <=0: #这个位置很关键 print('没有找到') return -1 if n < lis[middle]: lis=lis[:middle-1] #边界问题 elif n > lis[middle]: lis = lis[middle+1:]#中间位置 else: print('找到了') return lis func(lis,n) func(lis,1234) dic = {'怎么':'对方是否','1':123,'78':213} print(dic.keys())#dict_keys(['怎么', '1', '78']) 这个列表是高仿列表是不能够去 操作的 print(dic.items())#dict_items([('怎么', '对方是否'), ('1', 123), ('78', 213)])#同样的这个列表且套元组的是不能够去使用的 #三级菜单: dic = {'北京':{'朝阳':{'朝阳大妈':'朝阳纪委','朝阳群众':'描线'},'海淀':{'海淀区':'海胆'},'昌平':{'沙河':'黄河','黄山':'一'}}, '上海':{'上海滩':{'发哥':'赌神'},'歌剧院':{'歌星':'不知道'}}} 递归方法 def treeLM(dic): while 1: for i in dic:print(i) key = input('请输入你选择的地址:').strip() if key == 'b' or key == 'q':return key elif key in dic.keys() and dic[key]: ret =treeLM(dic[key]) if ret == 'q':return 'q' elif (not dic.get(key)) or (not dic[key]): continue else: print('好好输入') treeLM(dic) l =[dic] while l: for i in l[-1]:print(i) k = input('>>>:').strip() if k in l[-1].keys() and l[-1][k]:l.append(l[-1]) elif k == 'b':l.pop() elif k == 'q':break else:continue
lifo 先进后出 栈 用于递归的 的算法上 三级菜单
数据的安全:
对于list dic 这样的数据 通过增删改查 这样的操作 是不会发生数据不安全的,
而 -= += *= 这样的挥发生数据安全问题的, 因为在算当中 会开回切换, 对于GIL锁来说
只有一个进程把锁还回来 另外一个程序在可以被cpu运行调度 所以没有其他锁的情况下就会数据不安全
注意:主线程会等待子线程结束之后才结束
守护线程 :(代码t = Thread(target=func)
t.setDaemon(True) ---设置当前线程为守护线程
守护进程:守护进程会等待主进程的代码结束而结束
守护线程 :守护线程会等待主线程的结束而结束
如果主线程还开启了其他子线程,那么守护线程会守护到最后
主进程必须后结束,回收子进程的资源
线程是属于进程的,主线程如果结束了,那么整个进程就结束了
当只有主线程 与一条守护线程 的时候 主线程 直接运行结束,不用等守护线程,运行 ,因为守护线程本身就是为了守护主线程而产生的,主线程安全运行,那么守护线程的
的使命也就是随着结束了.
过程
主线程结束依赖两个事儿 (自己的代码执行完毕,非守护的子线程执行完毕)
-->主线程结束,进程也结束了
-->这个进程中的所有线程都结束了,所有的守护线程也就不存在了
锁:
在python中的线程 是不能利用多个CPU -- 全局解释器锁 GIL
import dis # 内置模块 能够查看python代码翻译成的机器语言
GIL锁:
GIL锁 :能够保证在同一时刻 不可能有两个线程 同时执行 CPU指令
面试题:
有了这个锁 多线程是不是就没用了?
socket recv accept 在高IO操作的时候多线程仍然比多进程好用很多很多
GIL全局解释器锁 是 Cpython解释器中的
Jpython pypy 都支持多线程访问多核
为什么这个锁要加上啊? 历史遗留问题 + python是解释型语言的现实情况
为什么不把这个锁去掉啊? 尝试着去掉过 效果不好
多线程不能利用多核这件事情如何弥补,如何看待?
1.不能利用多核 并不能说明在解决高IO操作的程序中python是有问题的
2.如果我要解决的是高计算的场景呢?
1.多进程 2.搭建集群和分布式系统