一、线程

1.线程

  1)、什么是线程

    线程是cpu调度的最小单位

    线程是进程的必要组成单位

      一个进程里至少含有一个线程

  2)、主线程

     程序开始运行的视乎,就产生了一个主线程来运行这个程序

  3)、子线程

    是由主线程开启的其他线程

  4)、各线程之间的工作

    异步的

    数据共享的

  5)、GIL Cpython全局解释器锁

    Cpython解释器,在同一个进程中的多个线程,每次只能有一个线程可以获得执行CPU的权限。

    这是由于Cpython的垃圾回收线程等原因导致

2.线程的开启

  #  线程不能在外界的干扰下结束,而是等待程序的执行完毕才结束,主线程要等待子线程的结束而结束。

  

from threading import  Thread,currentThread

def t_func():
global n
n -= 1
print(currentThread()) if __name__ == '__main__':
n = 100
t_lst = []
for i in range(100):
t = Thread(target=t_func)
t.start()
t_lst.append(t)
print(t.ident,t.name,t.is_alive())
for t in t_lst:t.join()
print(n)

3.守护线程

  # 守护线程,会等待主线程执行完毕才结束,主线程会等待所有子线程结束而结束。

  

from threading import Thread,currentThread,active_count,enumerate
import time def func1():
time.sleep(3)
print('in func1')
def func2():
while True:
time.sleep(0.5)
print('in func2') if __name__ == '__main__':
Thread(target=func1).start()
t = Thread(target=func2, daemon=True)
# t.setDaemon(True)
# t.daemon = True
t.start()
# print(enumerate())
# print(active_count())
# print(currentThread())
# print(t.getName())
# print(t.setName('zzz'))
# print(t.getName())
print('主线程') # 守护线程:会等待主线程执行完毕而结束,主线程会等待所有子进程结束后才结束

守护进程

4.锁

  区别GIL和锁,GIL只是同进程中的不同线程只能有一个线程访问CPU,而互斥锁,是保障数据安全的一种机制,二者不冲突。

  

from threading import Lock,RLock,Thread

# 数据不安全实例,由于global数据共享,导致两个线程可能仅计算完+8,还未写入,就被另一个线程抢到执行
# 权限被赋值为0
# def change_balance(n):
# global balance
# balance += n
# balance -= n
#
#
# def run_thread(n):
# for i in range(150000):
# change_balance(n)
#
#
# balance = 0
# t1 = Thread(target=run_thread, args=(5,))
# t2 = Thread(target=run_thread, args=(8,))
# t1.start()
# t2.start()
# t1.join()
# t2.join()
# print(balance) # 加锁 保障数据安全 def change_balance(n):
global balance
balance += n
balance -= n def run_thread(n):
for i in range(150000):
with lock:change_balance(n) balance = 0
lock = Lock()
t1 = Thread(target=run_thread, args=(5,))
t2 = Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

数据安全加锁

5.死锁和递归锁

  互斥锁和递归锁的区别:

    #互斥锁在同一个线程中连续acquire一次以上就会死锁

    #递归锁在同一个线程中可以连续的acquire多次而不发生死锁

  #普遍:递归锁可以代替互斥锁来解决死锁现象

  #实际上:递归锁的解决死锁实际上是牺牲了时间和空间的

    # 死锁从本质上来说是一种逻辑错误

    # 递归锁没有从根本上解决死锁问题

  递归锁画图描述:

  

from threading import Lock
# 互斥锁
# lock = Lock()
# lock.acquire()
# print(123)
# lock.release() from threading import RLock
# 递归锁
# lock = RLock()
# lock.acquire()
# lock.acquire()
# print(123)
# lock.release() # 死锁
# 科学家吃面问题
# import time
# from threading import Thread,Lock
#
# def eat1(name,fork_lock,noodle_lock):
# fork_lock.acquire()
# print('%s拿到叉子了'%name)
# noodle_lock.acquire()
# print('%s拿到面条了' % name)
# print('%s吃面'%name)
# noodle_lock.release()
# fork_lock.release()
#
# def eat2(name,fork_lock,noodle_lock):
# noodle_lock.acquire()
# print('%s拿到面条了' % name)
# time.sleep(1)
# fork_lock.acquire()
# print('%s拿到叉子了' % name)
# print('%s吃面'%name)
# fork_lock.release()
# noodle_lock.release()
#
# fork_lock = Lock()
# noodle_lock = Lock()
# Thread(target=eat1,args=('alex',fork_lock,noodle_lock)).start()
# Thread(target=eat2,args=('wusir',fork_lock,noodle_lock)).start()
# Thread(target=eat1,args=('yuan',fork_lock,noodle_lock)).start()
# Thread(target=eat2,args=('jin',fork_lock,noodle_lock)).start() # 递归锁解决死锁现象
# import time
# from threading import Thread,RLock
#
# def eat1(name,fork_lock,noodle_lock):
# fork_lock.acquire()
# print('%s拿到叉子了'%name)
# noodle_lock.acquire()
# print('%s拿到面条了' % name)
# print('%s吃面'%name)
# noodle_lock.release()
# fork_lock.release()
#
# def eat2(name,fork_lock,noodle_lock):
# noodle_lock.acquire()
# print('%s拿到面条了' % name)
# time.sleep(1)
# fork_lock.acquire()
# print('%s拿到叉子了' % name)
# print('%s吃面'%name)
# fork_lock.release()
# noodle_lock.release()
#
# noodle_lock = fork_lock = RLock()
# Thread(target=eat1,args=('alex',fork_lock,noodle_lock)).start()
# Thread(target=eat2,args=('wusir',fork_lock,noodle_lock)).start()
# Thread(target=eat1,args=('yuan',fork_lock,noodle_lock)).start()
# Thread(target=eat2,args=('jin',fork_lock,noodle_lock)).start() # import time
# from threading import Thread,RLock
#
# def eat1(name,lock):
# lock.acquire()
# print('%s拿到叉子了'%name)
# print('%s拿到面条了' % name)
# print('%s吃面'%name)
# lock.release()
#
# def eat2(name,lock):
# lock.acquire()
# print('%s拿到面条了' % name)
# time.sleep(1)
# print('%s拿到叉子了' % name)
# print('%s吃面'%name)
# lock.release()
#
# noodle_fork_lock = Lock()
# Thread(target=eat1,args=('alex',noodle_fork_lock)).start()
# Thread(target=eat2,args=('wusir',noodle_fork_lock)).start()
# Thread(target=eat1,args=('yuan',noodle_fork_lock)).start()
# Thread(target=eat2,args=('jin',noodle_fork_lock)).start() # 互斥锁和递归锁的区别 *****
# 互斥锁在同一个线程中连续acquire一次以上就会死锁
# 递归锁在同一个线程中可以连续的acquire多次而不发生死锁
# 普遍 :递归锁可以代替互斥锁来解决死锁现象 # 实际上 : 递归锁的解决死锁实际上是牺牲了时间和空间的 ****
# 死锁从本质上来讲是一种逻辑错误
# 递归锁没有从根本上解决死锁问题

6.事件

event.isSet():返回event的状态值;
event.wait():如果 event.isSet()==False将阻塞线程;
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.clear():恢复event的状态值为False。
from threading import Thread,Event,currentThread
import time,random def check_mysql():
print('\033[45m[%s]正在检查mysql' % currentThread().getName())
time.sleep(random.randint(0,2))
event.set() def conn_mysql():
count = 1
while not event.is_set():
if count > 3:
raise TimeoutError('链接超时')
print('%s第%s次尝试链接' %(currentThread().getName(),count))
time.sleep(0.5)
count += 1
print('<%s>连接成功' % currentThread().getName()) if __name__ == '__main__':
event = Event()
check = Thread(target=check_mysql)
conn1 = Thread(target=conn_mysql)
conn2 = Thread(target=conn_mysql)
check.start()
conn1.start()
conn2.start()

模拟检查连接mysql的例子

7.定时器

# 可以定时多少时间后开始执行线程中的任务。

from threading import Timer,Thread,currentThread

def func(n):
print('in func', currentThread().getName(),'args%s'%n) t = Timer(3,func,args=(1,))
t.start()
print(currentThread().getName())
from threading import Timer,Thread,currentThread
import time def func(n):
print('in func', currentThread().getName(),'args%s'%n) t = Timer(3,func,args=(1,))
t.start()
time.sleep(1)
print(t.is_alive())
print(currentThread().getName())

定时器的例子

8.条件

# 一次放行多少个线程,放完就没有了,顺序通过,

# 在acquire()和release必须一一对应,wait或notify之前必须有acquire()

from threading import Condition,Thread

def func(n):
con.acquire()
con.wait()
print('run the thread %s' % n)
con.release() if __name__ == '__main__':
con = Condition()
for i in range(10):
t = Thread(target=func,args=(i,))
t.start() while True:
inp = input('>>>')
if inp == 'q':
break
con.acquire()
con.notify(int(inp))
con.release()

      

9.队列(和threading模块没关系了)

  Queue(),PriorityQueue(),LifoQueue()

  

import queue
# q = queue.Queue() # 先进先出队列,维护先进先出顺序
# q.put(2)
# q.put(3)
# q.put(4)
# print(q.get())
# print(q.get())
# print(q.get()) # q = queue.LifoQueue() # 后进先出队列,栈
# q.put(2)
# q.put(3)
# q.put(4)
# print(q.get())
# print(q.get())
# print(q.get()) q = queue.PriorityQueue() # 优先级队列,优先级相同,根据ascii码先后 q.put((10, 2))
q.put((20, 3))
q.put((1, 4))
q.put((100, 'b'))
q.put((100, 'a')) print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())

三种队列举例

10.池

  multiprocessing中的Pool:

    # apply_async 异步提交任务

    # 必须close join之后才能维护主进程和进程池之间的同步

    # map自带close、join效果

    # 获取执行结果get

    # 回调函数 指定callback参数,由主进程执行

concurrent.futures.ThreadPoolExecutor/concurrent.futures.ProcessPoolExecutor

    # submit 异步提交任务

    # 使用shutdown来维护主进程和进程池之间的同步

    # map是不自带shutdown

    # 获取执行结果用result

    # 回调函数 直接调用add_done_callback方法,由子线程/子进程执行

  # 用concurrent.futures,可以轻松的在进程和线程之间切换(代码改个名字即可)

  #并发程序,线程池进程池都要用,只需要导入一个模块

  # concurrent.futures模块是一个新的模块,同意了线程池和进程池的使用方式,对一些操作进行了更合理的规划。

二、协程

    1)、协程--纤程,本质是线程的一部分

    2)、对比线程和协程

      #  线程,正常的线程,遇到阻塞就停下来,直到阻塞事件结束,才继续执行

      #  协程,利用了协程,就把线程分成了好几段,在一个任务出现阻塞的时候,自动切换到另一任务去执行,这些事情在一个线程里面完成。

    3)、如果这个程序从头到尾没有IO,没有阻塞,程序根本就不会在多个任务之间切换,就是一个顺序执行的协程,对于高计算型的代码没有用。

    4)、协程

        # 数据安全的问题不存在了

        # 如何调度的呢?

          # 操作系统不调度协程

          # 用户级别来调度的gevent

          # 协程的调度速度比线程还快

          # 协程的调度由于是python代码级别而不是操作系统级别,所有用户的可控性更强,降低了操作系统的工作量。

      5)、协程到底是怎么实现程序之间的切换的

      生成器:

      

def func():
print(123)
yield 1
print(456)
yield 2
print(789)
yield 3 def wahaha(g):
for i in g:
print(i) g = func()
wahaha(g)

生成器程序之间切换的例子

协程之间的切换:gevent依赖greenlet,协程之间的切换有greenlet完成

      

# def func():
# print(123)
# yield 1
# print(456)
# yield 2
# print(789)
# yield 3
#
#
# def wahaha(g):
# for i in g:
# print(i)
#
# g = func()
# wahaha(g) # 协程之间的切换 from greenlet import greenlet def eat(name):
print('%s eat 1' %name)
g2.switch('egon')
print('%s eat 2' %name)
g2.switch()
def play(name):
print('%s play 1' %name)
g1.switch()
print('%s play 2' %name) g1 = greenlet(eat)
g2 = greenlet(play) g1.switch('alex')

greenlet

from gevent import monkey
monkey.patch_all()
import time
import gevent # 能够在遇到自己认识的IO操作的时候主动调用greenlet中的switch来切换到其他的任务以提高程序的效率
from threading import currentThread
def eat(name):
print(currentThread())
print('%s eat 1' %name)
time.sleep(2)
print('%s eat 2' %name) def play(name):
print(currentThread())
print('%s play 1' %name)
time.sleep(1)
print('%s play 2' %name) g1=gevent.spawn(eat,'egon') # 就是发布了任务
g2=gevent.spawn(play,name='egon') # 就是发布了任务
# g1.join() # 等待g1任务执行完毕,阻塞,帮助你完成g1中函数的全部内容
# g2.join()
gevent.joinall([g1,g2])
print('主')

gevent

# 效率对比

from gevent import spawn, joinall, monkey;
monkey.patch_all() import time def task(pid):
time.sleep(0.5)
print('Task %s done' % pid) def synchronous(): # 同步
for i in range(10):
task(i) def asynchronous(): # 异步
# g_l = []
# for i in range(10):
# g = spawn(task,i)
# g_l.append(g)
# for g in g_l:g.join()
# joinall(g_l) joinall([spawn(task, i) for i in range(10)]) if __name__ == '__main__':
# print('Synchronous:')
# synchronous()
print('Asynchronous:')
asynchronous()

高IO同步异步效率对比

  
# 解决并发问题???
# 多进程  高计算型,浪费操作系统和资源,可以利用多核
# 多线程  高IO型,也会给操作系统添加负担,会占用比进程少的资源,
          # 受到GIL,不能利用多核
# 协程    高IO型,完全不会给操作系统添加负担,几乎不占资源
          # 始终不能利用多核

# 多进程 + 多线程  = 100
# 多进程 + 多线程 + 协程  = 50000/4c
    # 4c
    # 多进程 CPU+1  = 5
    # 多线程 CPU*5  = 20
    # 协程   500个协程

       

    

python全栈开发day35-线程、协程的更多相关文章

  1. Python全栈开发:线程、进程和协程

    Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/usr/bin/env pytho ...

  2. Python全栈开发:线程代码实例

    #进程与线程的关系 """ 多进程(主进程,子进程): 优点:能同时利用多个CPU,进行多个操作,提高效率. 缺点:耗费内存资源(进程要开辟内存空间),进程不是越多越好, ...

  3. python全栈开发目录

    python全栈开发目录 Linux系列 python基础 前端~HTML~CSS~JavaScript~JQuery~Vue web框架们~Django~Flask~Tornado 数据库们~MyS ...

  4. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  5. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  6. python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)

    python全栈开发笔记第二模块 第四章 :常用模块(第二部分)     一.os 模块的 详解 1.os.getcwd()    :得到当前工作目录,即当前python解释器所在目录路径 impor ...

  7. python全栈开发学习_内容目录及链接

    python全栈开发学习_day1_计算机五大组成部分及操作系统 python全栈开发学习_day2_语言种类及变量 python全栈开发_day3_数据类型,输入输出及运算符 python全栈开发_ ...

  8. Python 全栈开发【第0篇】:目录

    Python 全栈开发[第0篇]:目录   第一阶段:Python 开发入门 Python 全栈开发[第一篇]:计算机原理&Linux系统入门 Python 全栈开发[第二篇]:Python基 ...

  9. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  10. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

随机推荐

  1. linux的基本操作与常见命令

    linux的基本操作与常见命令: jdk的安装: 步骤:(特别注意:虚拟机安装的一般是32位的操作系统,jdk也必须使用32位的) 查看虚拟机版本:sudo uname --m i686 //表示是3 ...

  2. jq版轮播图

    html部分 <div class="banner"> <ul class="img"> <li><img src=& ...

  3. 19. SpringBoot_web开发-使用外部Servlet容器&JSP支持

    還沒有web.xml,生  配置tomcat 嵌入式Servlet容器:应用打成可执行的jar 优点:简单.便携: 缺点:默认不支持JSP.优化定制比较复杂 使用定制器[ServerPropertie ...

  4. luogu P3243 [HNOI2015]菜肴制作

    这题一看就知道和拓扑序有关 考虑拓扑排序的时候每次取队列里最小的数进行排序 然后就\(\mathcal{GG}\)了,因为这样只能使字典序最小,而并不能保证题目中要求的每个编号的数要在满足前面数尽量在 ...

  5. Android关于RAM、ROM、SD卡以及各种内存的区别

    RAM:运行时内存.相当于PC机的内存存储,用于存储应用运行时的各种对象和变量常量等,主要作用在于提高运行速度.是唯一一种断电后数据会清除的存储器. 机身内存:相当于PC机硬盘.主要包括三块区域:RO ...

  6. Spring之JDBCTemplate学习

    一.Spring对不同的持久化支持: Spring为各种支持的持久化技术,都提供了简单操作的模板和回调 ORM持久化技术 模板类 JDBC org.springframework.jdbc.core. ...

  7. malloc 函数详解【转】

    转自:https://www.cnblogs.com/Commence/p/5785912.html 很多学过C的人对malloc都不是很了解,知道使用malloc要加头文件,知道malloc是分配一 ...

  8. springboot系列五、springboot常用注解使用说明

    一.controller相关注解 1.@Controller 控制器,处理http请求. 2.@RespController Spring4之后新加的注解,原来返回json需要@ResponseBod ...

  9. shell脚本中冒号

    格式:: your comment here 格式:# your comment here 写代码注释(单行注释). 例如: 格式:: 'comment line1 comment line2 mor ...

  10. tomcat6和tomcat7管理用户manager配置

    tomcat用户登录文件配置 如果想要对部署在tomcat上的项目进行管理查看,需要在tomcat安装目录conf文件夹下的tomcat-user.xml里添加用户登录权限.具体添加的内容如下: To ...