百万年薪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)是面向对象软件技术当中的一个概念. 通俗易懂的理解是:子承父业,合法继承家产 专业的理解是:子类可以完全使用父类的方法和属 ...
随机推荐
- [LeetCode] 由 “分形" 所想
分形思想和递归思想有区别么? 一.简单例子 函数调用自己,简化了理解逻辑,但其他到处都是问题. #%% def listsum(numList): if len(numList) == 1: retu ...
- Java线程池的拒绝策略
一.简介 jdk1.5 版本新增了JUC并发编程包,极大的简化了传统的多线程开发.前面文章中介绍了线程池的使用,链接地址:https://www.cnblogs.com/eric-fang/p/900 ...
- Codeforces Numbers 题解
这题只需要会10转P进制就行了. PS:答案需要约分,可以直接用c++自带函数__gcd(x,y). 洛谷网址 Codeforces网址 Code(C++): #include<bits/std ...
- linux 进程消耗查看
Linux下如何查看哪些进程占用的CPU内存资源最多 linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合: ps aux|head -1;ps aux|grep -v PID|sor ...
- 1.python环境配置 - python基础入门
工欲善其事必先利其器,python学习首先要做得就是配置python环境.配置环境只需要下载Pycharm 和 Anaconda两个安装包即可,请跟上我得步伐,一步一步操作. 重要的事情说三遍: 先安 ...
- linux常用开发命令总结
linux常用命令 文件操作命令 1. cd 目录名/目录名 切换目录 cd .. 切换到上一级目录 (change dictionary) Ctrl+C强制退出命令行,回到上一级 2.ls ...
- Angular template ng-template/container/content
1. ng-template 形式:<ng-template>...</ng-template> 默认ng-template中的内容会隐藏; 可通过[ngIf]来控制内容显示隐 ...
- linux 装composer的出现的问题
curl -sS https://getcomposer.org/installer | php php 值得是php的liux下的安装目录 php环境变量 当装compser 的时候,出现 ...
- js 验证数据类型的4中方法
1.typeof 可以检验基本数据类型 但是引用数据类型(复杂数据类型)无用: 总结 : typeof 无法识别引用数据类型 包括 bull; 2.instanceof是一个二元运算符,左操作数 ...
- 从零开始入门 K8s | 应用存储和持久化数据卷:存储快照与拓扑调度
作者 | 至天 阿里巴巴高级研发工程师 一.基本知识 存储快照产生背景 在使用存储时,为了提高数据操作的容错性,我们通常有需要对线上数据进行 snapshot ,以及能快速 restore 的能力.另 ...