百万年薪python之路 -- 并发编程之 多线程 一
多线程
1.进程: 生产者消费者模型
一种编程思想,模型,设计模式,理论等等,都是交给你一种编程的方法,以后遇到类似的情况,套用即可
生产者与消费者模型的三要素:
- 生产者:产生数据的
- 消费者:接收数据做进一步处理的
- 容器: 缓存区(队列) 起到缓冲的作用,平衡生产力与消费者,解耦
2.线程的理论知识
什么是线程
一条流水线的工作流程
进程: 在内存中开启一个进程空间,然后将主进程的所有的资源复制一份,然后调用cpu去执行这些代码.
进程是资源调度的基本单位,而线程是cpu的最小执行单位
进程的开启: 进程会在内存中开辟一个进程空间,将主进程的数据全部复制一份,线程会执行里面的代码
线程vs进程
- 开启进程的开销非常大,比线程的开销大很多.
- 开启线程的速度非常快,要比进程快几十倍到上百倍.
- 同一个进程内,线程与线程之间可以共享数据, 进程与进程之间需要借助队列等方法实现通信.
线程的应用
并发: 一个cpu看起来像同时执行多个任务
单个进程开启三个线程,并发的执行任务.
开启三个进程并发的执行任务.
开启多线程的优点: 数据共享,开销小,速度快.
主线程和子线程没有主次之分
那么一个进程谁在干活?
一个主线程在干活,当主线程执行完代码后,还得等待其他线程执行完,才能退出进程.
3.开启线程的两种方式
**线程不需要在if _ _ name _ _ == '_ _ main _ _':语句下**
第一种:
from threading import Thread
import time
def task(name):
print(f"{name} is running")
time.sleep(1)
print(f"{name} is gone")
if __name__ == '__main__':
t1 = Thread(target=task,args=("zcy",))
t1.start()
print("==main Threading==") # 线程没有主次之分
第二种:
from threading import Thread
import time
class MyThread(Thread):
def __init__(self,name,lst,s):
super(MyThread, self).__init__()
self.name = name
self.lst =lst
self.s = s
def run(self):
print(f"{self.name} is running")
time.sleep(1)
print(f"{self.name} is gone")
if __name__ == '__main__':
t1 = MyThread("zdr",[1,2,3],"180")
t1.start()
print("==main thread==")
4.线程vs进程的代码对比
开启速度对比
# 多进程 from multiprocessing import Process def work(): print('hello') def task(): print('bye') if __name__ == '__main__': # 在主进程下开启线程 t1 = Process(target=work) t2 = Process(target=task) t1.start() t2.start() print('main thread/process')
# 多线程 from threading import Thread import time def task(name): print(f"{name} is running") time.sleep(1) print(f"{name} is gone") if __name__ == '__main__': t1 = Thread(target=task,args=("zdr",)) t2 = Thread(target=task,args=("zcy",)) t3 = Thread(target=task,args=("zfy",)) t4 = Thread(target=task,args=("lfz",)) t1.start() t2.start() t3.start() t4.start() print('==main thread==') # 线程是没有主次之分
对比pid
# 进程 from multiprocessing import Process import time import os def task(): print(f"子进程:{os.getpid()}") print(f"主进程:{os.getppid()}") if __name__ == '__main__': p1 = Process(target=task) p2 = Process(target=task) p1.start() p2.start() print(f"==main{os.getpid()}")
# 主线程 from threading import Thread import os def task(): print(os.getpid()) if __name__ == '__main__': t1 = Thread(target=task) t2 = Thread(target=task) t1.start() t2.start() print(f"===main thread:{os.getpid()}")
同一个进程内线程共享内部数据
from threading import Thread import os x = 3 def task(): global x x = 100 if __name__ == '__main__': t1 = Thread(target=task) t1.start() print(f"===main thread:{x}") # 同一个进程内的资源数据对于这个进程的多个线程来说是共享的.
小练习:
import multiprocessing
import threading
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',8080))
s.listen(5)
def action(conn):
while True:
data=conn.recv(1024)
print(data)
conn.send(data.upper())
if __name__ == '__main__':
while True:
conn,addr=s.accept()
p=threading.Thread(target=action,args=(conn,))
p.start()
多线程并发的socket服务端
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
s.send(msg.encode('utf-8'))
data=s.recv(1024)
print(data)
客户端
5.线程的相关其他方法
线程对象的方法:
线程.isAlive() # 判断线程是否存活
线程.getname() # 获取线程名
线程.setname() # 设置线程名 ***
threading模块的方法:
threading.currentThread() # 获取当前进程的对象
threading.enumerate() # 返回一个列表,包括所有的线程对象
threading.activeCount() # 返回一个数字,表示有多少个线程还存活
from threading import Thread, currentThread, enumerate,activeCount
import os
import time
x = 3
def task():
# print(currentThread())
# time.sleep(1)
print("123")
if __name__ == '__main__':
t1 = Thread(target=task,name="xc-1")
t2 = Thread(target=task,name="xc-2")
# name 设置线程名
t1.start()
t2.start()
# time.sleep(2)
# print(t1.isAlive()) # 判断线程是否存活
# print(t1.getName()) # 获取线程名
# t1.setName("zcy-01")
# print(t1.name) # ***
# threading方法
# print(currentThread()) # 获取当前线程的对象
# print(currentThread().name) # 获取当前线程的对象
print(enumerate()) # 返回一个列表,包含所有的线程对象
print(activeCount())
print(f"===main thread:{os.getpid()}")
6.join和守护线程
join: 阻塞 告知主线程要等待子线程执行完毕之后再执行主线程
# 线程join
from threading import Thread
import time
def task(name):
print(f"{name} is running")
time.sleep(1)
print(f'{name} is gone')
if __name__ == '__main__':
start_time = time.time()
t1 = Thread(target=task,args=("zdr",))
t2 = Thread(target=task,args=("zcy",))
t3 = Thread(target=task,args=("zfy",))
t1.start()
t1.join()
t2.start()
t2.join()
t3.start()
t3.join()
print(f"===main thread:{time.time() - start_time}")
守护线程:
无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
需要强调的是:运行完毕并非终止运行
#1.对主进程来说,运行完毕指的是主进程代码运行完毕
#2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
详细解释:
#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束,
#2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
先对比一下守护进程:
from multiprocessing import Process
import time
def foo():
print(123)
time.sleep(1)
print("end123")
def bar():
print(456)
time.sleep(2)
print("end456")
if __name__ == '__main__':
p1 = Process(target=foo)
p2 = Process(target=bar)
p1.daemon = True
p1.start()
p2.start()
print('====main====')
守护线程:
from threading import Thread
import time
def sayhi(name):
print('bye~')
time.sleep(2)
print(f'{name} say hello ')
if __name__ == '__main__':
t = Thread(target=sayhi,args=('zcy',))
# t.setDaemon(True)
t.daemon = True
t.start()
print('主线程')
from threading import Thread
import time
def foo():
print(123) # 1
time.sleep(1)
print('end123') # 4
def bar():
print(456) # 2
time.sleep(3)
print('en456') # 3
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.daemon = True
t1.start()
t2.start()
print('=====main====') # 3
结果:
123
456
=====main====
end123
en456
# 主线程什么时候结束?
# 主线程等待非守护子线程结束之后,结束
from threading import Thread
import time
def foo():
print(123) # 1
time.sleep(3)
print("end123")
def bar():
print(456) # 2
time.sleep(1)
print("end456") # 4
t1=Thread(target=foo)
t2=Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print("main-------") # 3
结果:
123
456
main-------
end456
7.互斥锁
from threading import Thread
import time
import random
x = 100
def task():
time.sleep(random.randint(1,2))
global x
temp = x
time.sleep(random.randint(1,3))
temp = temp - 1
x = temp
if __name__ == '__main__':
l = []
for i in range(100):
t = Thread(target=task)
l.append(t)
t.start()
for i in l:
i.join()
print(f"main:{x}")
# 多个任务共抢一个数据,要保证数据的安全性,要让他们串行
# 给线程加锁
from threading import Thread
from threading import Lock
import time
import random
x = 100
def task(lock):
lock.acquire()
# time.sleep(random.randint(1,2))
global x
temp = x
time.sleep(0.01)
temp = temp - 1
x = temp
lock.release()
if __name__ == '__main__':
mutex = Lock()
l1 = []
for i in range(100):
t = Thread(target=task,args=(mutex,))
l1.append(t)
t.start()
time.sleep(3)
print(f'主线程{x}')
百万年薪python之路 -- 并发编程之 多线程 一的更多相关文章
- 百万年薪python之路 -- 并发编程之 多线程 二
1. 死锁现象与递归锁 进程也有死锁与递归锁,进程的死锁和递归锁与线程的死锁递归锁同理. 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因为争夺资源而造成的一种互相等待的现象,在无外力的作用 ...
- 百万年薪python之路 -- 并发编程之 多线程 三
1. 阻塞,非阻塞,同步,异步 进程运行的三个状态: 运行,就绪,阻塞. 从执行的角度: 阻塞: 进程运行时,遇到IO了,进程挂起,CPU被切走. 非阻塞: 进程没有遇到IO 当进程遇到IO, ...
- 百万年薪python之路 -- 并发编程之 多进程 一
并发编程之 多进程 一. multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大 ...
- 百万年薪python之路 -- 并发编程之 协程
协程 一. 协程的引入 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两 ...
- 百万年薪python之路 -- 并发编程之 多进程二
1. 僵尸进程和孤儿进程 基于unix的环境(linux,macOS) 主进程需要等待子进程结束之后,主进程才结束 主进程时刻检测子进程的运行状态,当子进程结束之后,一段时间之内,将子进程进行回收. ...
- 百万年薪python之路 -- 数据库初始
一. 数据库初始 1. 为什么要有数据库? 先来一个场景: 假设现在你已经是某大型互联网公司的高级程序员,让你写一个火车票购票系统,来hold住十一期间全国的购票需求,你怎么写? 由于在同一时 ...
- 百万年薪python之路 -- Socket
Socket 1. 为什么学习socket 你自己现在完全可以写一些小程序了,但是前面的学习和练习,我们写的代码都是在自己的电脑上运行的,虽然我们学过了模块引入,文件引入import等等,我可以在程序 ...
- 百万年薪python之路 -- 面向对象之三大特性
1.面向对象之三大特性 1.1封装 封装:就是把一堆代码和数据,放在一个空间,并且可以使用 对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封 ...
- 百万年薪python之路 -- 面向对象之继承
面向对象之继承 1.什么是面向对象的继承 继承(英语:inheritance)是面向对象软件技术当中的一个概念. 通俗易懂的理解是:子承父业,合法继承家产 专业的理解是:子类可以完全使用父类的方法和属 ...
随机推荐
- nested exception is java.lang.NoClassDefFoundError: javax/xml/soap/SOAPElement
JavaSE 8 includes package java.xml.soap.JavaSE 9 moved package javax.xml.soap to the module java.xml ...
- Hadoop入门 之 Hadoop常识
1.Hadoop是什么? 答:Hadoop是开源的分布式存储和分布式计算平台. 2.Hadoop的组成是什么? 答:Hadoop由HDFS和MapReduce这两个核心部分组成. HDFS(Hadoo ...
- 第六届蓝桥杯java b组第十题
10.压缩变换(程序设计) 小明最近在研究压缩算法. 他知道,压缩的时候如果能够使得数值很小,就能通过熵编码得到较高的压缩比. 然而,要使数值很小是一个挑战. 最近,小明需要压缩一些正整数的序列,这些 ...
- for for in 给已有的li绑定click事件生成新的li也有click事件
想要给已有的li元素绑定一个click事件,点击生成新的li元素,并且新的li元素也要有click事件 //不能用for循环给每个li绑定click事件 因为这样的话 后面新生成的li就没有click ...
- jenkins+svn+Ant+tomcat+非maven项目构建
首先,输入项目名称,创建一个自由风格的项目; 然后,配置旧项目的策略参数,目的是防止构建项目太多,占用资源. 下一步,jdk版本选择: 下一步,关联svn项目. 下一步:配置ant 看不清,再来一张. ...
- mysql 最左前缀匹配原则
1.在mysql建立联合索引时会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,示例:对列col1.列col2和列col3建一个联合索引 KEY index_col1_co ...
- linux查看系统的一些版本号指令
1.查看系统 [root@iZbp1eoiap1e1jb6pvo390Z ~]# lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch ...
- 只要听说过电脑的人都能看懂的网上pdf全书获取项目
作者:周奇 最近我要获取<概统>的教材自学防挂科(线代已死),于是我看到 htt链ps:/链/max链.book接118接.com接/html/2018/0407/160495927.sh ...
- wordpress发送邮件
首先在wordpress内添加SMTP协议的插件,我这里用的是WP Mail SMTP 配置如下 配置完成之后测试一下,一定要测试能否发邮件
- ELK 学习笔记之 elasticsearch Shard和Segment概念
Shard和segment概念: 转载: http://blog.csdn.net/likui1314159/article/details/53217750 Shard(分片) 一个Shard就是一 ...