Python基础 - 多线程(上)
前面对 进程 一点认识, 通俗理解, 进程是操作系统(OS)进行资源调度分配的基本单元. 每个程序的至少就一个进程在OS中被"监控"着的哦. 然后围绕着多进程, 用消息队列共享全局变量, 守护主进程, 进程池...这一通探讨, 当然还是偏向应用的一方, 我自己偶尔工作有多任务的处理的地方, 也是优先弄多进程 (主要是公司电脑贼强, 我就要弄多进程, 就要浪费资源哈哈..).
进程 呢, 基本没用过, (爬虫除外, 之前有用 scrapy 是多线程的), 自己来手写应该是没有的. 为啥宁愿优先考虑进程呢, 原因是我们大多用的 Python 的解释是 CPython. 它默认下, 其实是有设阻塞的, 在多线程一块, 其实 CPython 没有真正地能实现 多线程.. 先不说这...
也是从通俗的角度来理解的话, 一个进程(程序), 至少有一个进程. 进程和线程, 可以看作一个 1 : n 的关系.
OS 的调度, 有多个进程, 每个进程, 有多个线程 . 这样理解这种 "数量" 关系应该还可以.
单线程
从代码的视角, 即一段代码按顺序执行, 就是个一个单进程.
import time
# 我每天下班之后首先是: 煮饭, 做菜
def cook_rice():
for i in range(3):
print(f"cook rice ...{i}")
time.sleep(1)
def cook_dishes():
for i in range(4):
print(f"cook dishes...{i}")
time.sleep(0.5)
if __name__ == '__main__':
cook_rice()
cook_dishes()
cook rice ...0
cook rice ...1
cook rice ...2
cook dishes...0
cook dishes...1
cook dishes...2
cook dishes...3
这结果跟咱想的没毛病. 先做饭, 然后再做菜... but, 真实中是这样的嘛? 真实是 我先插上饭, 然后打开电脑, 放点音乐啥的, 就着手去洗菜, 切菜这些事情了.
可见真是情况是, 对于做饭这个程序, 我其实是可以同时做的. 这也是前面提到的多任务呀, 从生活来看, 这是非常自然的事情.而我一直观点是, 写代码的本质, 是对现实世界的抽象和模拟. 因此, 洗菜做饭这种每天都要面对的基本事情, 同样在编程从, 自然是归类为 Python 基础了.
多线程
也是用Python内置的一 threading 模块 的 Thread 类, 来演示一波哦, 做饭和做菜是可以一起弄的.
import time
import threading
# 我每天下班之后首先是: 煮饭, 做菜
def cook_rice():
for i in range(3):
print(f"cook rice ...{i}")
time.sleep(1)
def cook_dishes():
for i in range(4):
print(f"cook dishes...{i}")
time.sleep(0.5)
if __name__ == '__main__':
# 假设就各创一个线程来弄
t1 = threading.Thread(target=cook_rice)
t2 = threading.Thread(target=cook_dishes)
# 启动多线程
t1.start()
t2.start()
cook rice ...0
cook dishes...0
cook dishes...1
cook rice ...1
cook dishes...2
cook dishes...3
cook rice ...2
可以看到进程是不断在 "切换", 用进程的术语说, 多个进程的在进行 "任务调度" , 这里线程, 就, 嗯, 多个线程一起运行吧. (其实并未真正多个线程同时运行, 只是调度太快而已 cpu).
主线程 - 等待所有子线程
跟多进程是一样的, 主线程, 会默认等待所有的子进程, 都结束后才, 程序才会真正结束.
然后在创建线程, 传参也是一样的, 两种方式 * args 和 ** kwargs. 这些都没啥, 大致有印象就行, 理解这个过程和特性更为重要, 怎么写, 会百度就好.
import time
import threading
# 我每天下班之后首先是: 煮饭, 做菜
def cook_rice(n):
for i in range(n):
print(f"cook rice ...{i}")
time.sleep(1)
def cook_dishes(n):
for i in range(n):
print(f"cook dishes...{i}")
time.sleep(0.5)
if __name__ == '__main__':
# 假设就各创一个线程来弄
t1 = threading.Thread(target=cook_rice, args=(3,))
t2 = threading.Thread(target=cook_dishes, kwargs={"n":4})
# 启动多线程
t1.start()
t2.start()
print("main threading done ....")
cook rice ...0
cook dishes...0
main threading done ....
cook dishes...1
cook rice ...1
cook dishes...2
cook dishes...3
cook rice ...2
守护主线程
也是跟进程那块一样的, 从代码视角看, 即设置一个属性, daemon = True 即可.
import time
import threading
def cook_rice(n):
for i in range(n):
print(f"cook rice ...{i}")
time.sleep(1)
if __name__ == '__main__':
# 将 daemon 参数 传为 True 即设置为了守护主线程.
t1 = threading.Thread(target=cook_rice, args=(4,), daemon=True)
# 启动多线程
t1.start()
time.sleep(2)
print("main threading done ....")
cook rice ...0
cook rice ...1
cook rice ...2
main threading done ....
我在测试的时候, 这部分发现线程和进程有一个不一样的点, 在多个线程中, 如果只将其中一个线程给 daemon 后, 当主进程结束, 其余的线程还是会继续执行; 而在多个进程中, 如果将其中一个进程给 daemon 后, 整个任务也就跟着停掉了. 很奇怪, 尚未弄明白这一点,暂时, 我后续再测测看.
阻塞-等待子进程结束
其实这有些莫名其妙, 直接运行不设置 daemon 不就行了吗, 主要是啥, 我单纯想熟悉下 join() 这个 API 而已啦.
import time
import threading
# 我每天下班之后首先是: 煮饭, 做菜
def cook_rice(n):
for i in range(n):
print(f"cook rice ...{i}")
time.sleep(1)
if __name__ == '__main__':
# 假设就各创一个线程来弄
t1 = threading.Thread(target=cook_rice, args=(4,), daemon=True)
# 启动多线程
t1.start()
time.sleep(2)
# 阻塞等待, 子进程结束
t1.join()
print("main threading done ....")
cook rice ...0
cook rice ...1
cook rice ...2
cook rice ...3
main threading done ....
自定义线程
没啥应用, 也是纯练习下编程技巧而已. 工作也偶尔会遇到这样的情况, 就某个库的某个功能, 可能不能满足我的业务需求, 但我又不能全部写一遍, 或者去 改掉它的源代码, 改源代码这事情我干过, 那时候还是小白没经验, 最后导致, 只有我一个人能用, 很尴尬去改源代码....
正确的做法是: 创建一个自定义类, 去继承要修改的类, 然后 重写其某个方法 . 这是正确的. 这里呢要自定义, 其实就继承上面调用的这 threading.Tread 类, 然后 start() 的部分, 点进去看一下源码就知道了, 其实去 start() 会调用 它上面的 run( ) 方法, 因此, 重写 run( ) 方法即可.
import time
import threading
class MyTread(threading.Thread):
def __init__(self, *args):
super().__init__()
self.args = args
@staticmethod
def cook_rice(n):
for i in range(n):
print(f"cook rice ...{i}")
def cook_dishes(self):
for i in range(self.args[0]):
print(f"cook dishes with {i}")
def run(self):
self.cook_rice(self.args[0])
self.cook_dishes()
if __name__ == '__main__':
t1 = MyTread(3)
t1.start()
t1.join() # 等待子进程
print("done")
上篇就这吧, 也是对主线程的一个基本认识即可, 当然前提是对前面的 进程也有所了解, 这样对比起来学习是非常快的. 这些代码模板, 守护主线程, 阻塞, 继承重写.. 都跟进程是一样的哦,
而下篇呢, 就会聊点跟进程不一样的地方,如线程中 资源能共享, 已经资源竞争, 互斥锁这些概念,, 还是蛮有趣的哦.
Python基础 - 多线程(上)的更多相关文章
- 从零开始学Python第一周:Python基础(上)
Python语法基础(上) 一,Python的变量 (1)创建变量 变量的含义:存储信息的地方 创建变量并赋值 x = 1 print x x = 123 #再次赋值 print x (2)使用变量 ...
- Python基础(上)
前言 正式开始Python之旅,主要学习内容专注在爬虫和人工智能领域,如Web开发之类将跳过不研究. Python的意思是蟒蛇,源于作者Guido van Rossum(龟叔)喜欢的一部电视剧.所以现 ...
- python --- 基础多线程编程
在python中进行多线程编程之前必须了解的问题: 1. 什么是线程? 答:线程是程序中一个单一的顺序控制流程.进程内一个相对独立的.可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程 ...
- python基础===多线程
https://www.cnblogs.com/wj-1314/p/8263328.html threading 模块 先上代码: import time, threading def loop(): ...
- Python基础-多线程与多进程
一,线程与进程之间的关系:(从知乎上看到的) 一个必须知道的事实:执行一段程序代码,实现一个功能的过程介绍 ,当得到CPU的时候,相关的资源必须也已经就位,就是显卡啊,GPS啊什么的必须就位,然后CP ...
- Python基础Day1—上
一.计算机基础 CPU:中央处理器,相当于人的大脑:运算中心与控制中心的结合. 内存:临时存储数据,与CPU交互. 硬盘:永久存储数据. 内存的优点:读取速度快 内存的缺点:容量小,造价高,断电数据会 ...
- 吾八哥学Python(三):了解Python基础语法(上)
学习一门开发语言首先当然是要熟悉它的语法了,Python的语法还算是比较简单的,这里从基础的开始了解一下. 标识符1.第一个字符必须是字母表中字母或下划线'_'.2.标识符的其他的部分有字母.数字和下 ...
- python基础15上_迭代器_生成器
# 迭代器和生成器 # 迭代器: # 双下方法 : 很少直接调用的方法.一般情况下,是通过其他语法触发的 # 可迭代的 —— 可迭代协议 含有__iter__的方法('__iter__' in dir ...
- Python 用多线程上传和下载文件
# -*- coding: utf-8 -*- __author__ = 'louis' from ftplib import FTP import multiprocessing import ti ...
- 测验2: Python基础语法(上) (第4周)
快乐的数字 描述 编写一个算法来确定一个数字是否“快乐”. 快乐的数字按照如下方式确定:从一个正整数开始,用其每位数的平方之和取代该数,并重复这个过程,直到最后数字要么收敛等于1且一直等于1,要么将无 ...
随机推荐
- Failed to start: app/proxyman/inbound: failed to listen TCP on 10808
问题现象 启动 v2xxx-With-Core 失败,报错信息如下: 2023/08/03 11:38:56 [Info] infra/conf/serial: Reading config: F:\ ...
- docker - [12] 镜像发布到DockerHub、阿里云
题记部分 一.镜像发布到 DockerHub 1.地址:https://hub.docker.com/ 注册自己的账号 2.确定这个账号可以登录 3.在服务器上提交镜像 4.登录之后提交镜像即可. [ ...
- vue练习用免费开源api大全
1. 网易云api 网易云api是网上一位大神工具网易云获取的,数据都是真实的网易云数据 2. api大全 这是csdn一个兄弟收集的,种类挺多,就是有一些需要money,不过大部分还是免费的 3. ...
- 最简单的方式:如何在wsl2上配置CDUA开发环境
step0:序言 这篇文章可以帮助你以一个最为简单的方式迈出CUDA的第一步,从此一入CUDA深似海,从此头发是路人. 前提:你需要在Windows 11上: 电脑中有nvidia显卡以及驱动,由于w ...
- 震惊!C++程序真的从main开始吗?99%的程序员都答错了
嘿,朋友们好啊!我是小康.今天咱们来聊一个看似简单,但实际上99%的C++程序员都答错的问题:C++程序真的是从main函数开始执行的吗? 如果你毫不犹豫地回答"是",那恭喜你,你 ...
- 解决PyCharm提示Error: Please select a valid Python interpreter
前言 Pycharm运行Python3.7.8的程序时发现源程序运行报错(非语法错误) Error:please select a valid Python interpreter 解决 第一步:打开 ...
- Golang windows下 交叉编译
前言 在进行Go开发的时候,go env 可以设置Go的环境变量信息 GOOS 的默认值是我们当前的操作系统, 如果 windows,linux,注意 mac os 操作的上的值是darwin. GO ...
- Linux指令详解之:ctl相关命令大礼包
目录 6.4 服务管理命令(ctl大礼包) 6.4.1 systemctl 6.5.2 systemctl小结 6.5.3 timedatectl 6.5.4 localectl 6.5.5 netw ...
- Proxmox ve(Pve) 安装windows server
1.安装proxmox ve点击直达 官网地址 下载下来如果下载速度太慢 可以去安装个IDM https://www.52pojie.cn/thread-1013874-1-1.html 然后需要制作 ...
- win10/11 禁用移动热点,无法启用
将网络重制即可