百万年薪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)是面向对象软件技术当中的一个概念. 通俗易懂的理解是:子承父业,合法继承家产 专业的理解是:子类可以完全使用父类的方法和属 ...
随机推荐
- php数据提交POSt
通常情况下用户使用浏览器网页表单向服务器post提交数据,我们使用PHP的$_POST接收用户POST到服务器的数据,并进行适当的处理.但有些情况下,如用户使用客户端软件向服务端php程序发送post ...
- 浅谈HDFS(一)
产生背景及定义 HDFS:分布式文件系统,用于存储文件,主要特点在于其分布式,即有很多服务器联合起来实现其功能,集群中的服务器各有各的角色 随着数据量越来越大,一个操作系统存不下所有的数据,那么就分配 ...
- Django之choices选项和富文本编辑器的使用
项目准备 1.创建数据库 create database choices_test default charset utf8; 2.创建一个名为 choices_test 的Django项目: 3.创 ...
- 第六届蓝桥杯java b组第五题
九数组分数 1,2,3…9 这九个数字组成一个分数,其值恰好为1/3,如何组法? 下面的程序实现了该功能,请填写划线部分缺失的代码. public class A { public static vo ...
- 要不要学习Git(分布式版本控制系统)
做技术的人,要不要学一学Git呢? 提出这个问题,是因为很多小伙伴还不会使用Git. 对于任何新一代的技术工具,它在业界普及都有一个过程,Git的阻碍是:学习成本.工具迭代的成本. SVN诞生于200 ...
- SpringCloudEureka入门
说明 SpringBoot版本 2.1.7.RELEASE SpringCloud版本 Greenwich.SR2 创建eureka server工程 加入pom依赖 <dependencies ...
- 移动端获取短信验证码java实现——阿里云短信服务
需求:移动端输入手机号,获取验证码.点击登录,验证验证码是否输入错误.是否超时等情况,一旦校验通过,将用户数据保存到数据中(业务逻辑). 前提:注册阿里用户,开通短信服务,申请key.秘钥.签名.短信 ...
- 对vue nextTick深入理解-vue性能优化、DOM更新时机、事件循环机制
一.定义[nextTick.事件循环] nextTick的由来: 由于VUE的数据驱动视图更新,是异步的,即修改数据的当下,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图 ...
- 前端get和post那些事
首先,简单介绍下,get和post请求方法,综合以往笔记,现整理如下: 一.HTTP请求比较: 两种在客户端和服务器端进行请求-响应的方法是:GET和POST. GET - 从指定的资源请求数据 PO ...
- mysql创建用户和权限管理
一 权限管理 我们知道我们的最高权限管理者是root用户,它拥有着最高的权限操作.包括select.update.delete.update.grant等操作.那么一般情况在公司之后DBA工程师会创建 ...