参考学习方法:
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]
' % (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.搭建集群和分布式系统