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")

上篇就这吧, 也是对主线程的一个基本认识即可, 当然前提是对前面的 进程也有所了解, 这样对比起来学习是非常快的. 这些代码模板, 守护主线程, 阻塞, 继承重写.. 都跟进程是一样的哦,

而下篇呢, 就会聊点跟进程不一样的地方,如线程中 资源能共享, 已经资源竞争, 互斥锁这些概念,, 还是蛮有趣的哦.

原文地址:https://www.cnblogs.com/chenjieyouge/p/12393106.html

时间: 2024-11-05 21:59:33

Python基础 - 多线程(上)的相关文章

从零开始学Python第一周:Python基础(上)

Python语法基础(上) 一,Python的变量 (1)创建变量 变量的含义:存储信息的地方 创建变量并赋值 x = 1 print x x = 123 #再次赋值 print x (2)使用变量 x = 1 y = 2 z = 3 print x,y,x*y,z (3)变量的命名规则 由字母,数字,下划线组成 不能以数字开头 不能使用Python关键字 错误的变量命名举例: #name = 1 3k = 1 print = 1 英文字母大小写敏感,例如:m与M不同 m = 1 print M

Python基础(上)

前言 正式开始Python之旅,主要学习内容专注在爬虫和人工智能领域,如Web开发之类将跳过不研究. Python的意思是蟒蛇,源于作者Guido van Rossum(龟叔)喜欢的一部电视剧.所以现在开始暂时忘掉.NET忘掉C#,using干什么用的?不知道.... 我只记得.我要玩蛇!!! Python基础概述 1.优点 简单:Python是一种代表简单主意的语言.Python这种伪代码本质是它最大的优点之一,它使你能够专注于解决问题而不是搞明白语言本身. 易学:Python极易上手,它有着

python基础---多线程

多线程 1 线程: 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位 多线程: 多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源 1. 多线程共享一个进程的地址空间 2. 线程比进

吾八哥学Python(三):了解Python基础语法(上)

学习一门开发语言首先当然是要熟悉它的语法了,Python的语法还算是比较简单的,这里从基础的开始了解一下. 标识符1.第一个字符必须是字母表中字母或下划线'_'.2.标识符的其他的部分有字母.数字和下划线组成.3.标识符对大小写敏感. 保留字保留字就是关键字,不能用它们做任何标识符.Python里通过有一个keyword 模块,执行keyword.kwlist可以输出当前版本的所有保留字,如下: ['False', 'None', 'True', 'and', 'as', 'assert', '

python基础 多线程threading join 守护线程setDeamon 递归锁Rlock

开篇大概介绍多线程与多进程区别,详细的理论区别自己可以在其它博客搜一下,这里不再赘述 同一进程下的多个线程共享内存数据,多个线程之间没有主次关系,相互之间可以操作:cpu执行的都是线程,默认程序会开一个主线程:进程是程序以及和程序相关资源的集合:某些场景下我们可以使用多线程来达到提高程序执行效率的目的,下面就多线程的一些基础知识做简要说明 简单的多线程 1 import threading, time 2 3 def test1(x): 4 time.sleep(5) 5 print(x**x)

Python 用多线程上传和下载文件

1 # -*- coding: utf-8 -*- 2 __author__ = 'louis' 3 4 from ftplib import FTP 5 import multiprocessing 6 import time 7 8 9 def ftpconnect(): 10 ftp = FTP() 11 timeout = 30 12 port = 22 13 ftp.connect('localhost',port,timeout) # 连接FTP服务器 14 ftp.login('u

萌新向Python数据分析及数据挖掘 第一章 Python基础 (上)未排版

因word和博客编辑器格式不能完全对接,正在重新排版,2019年1月1日发出第一章完整版 本文将参考<Python编程 从入门到实践>的讲述顺序和例子,加上自己的理解,让大家快速了解Python的基础用法,并将拓展内容的链接添加在相关内容之后,方便大家阅读. 好了!我们开始第一章的学习. 第一章 Python基础 python安装以及环境搭建 python的安装和环境变量的配置通过百度查询即可解决,这里不作赘述. IDE的选择:因为后期需要用来做数据分析,所以直接安装Anaconda会是一个不

python基础知识~多线程

一 分类   伪并发 由于执行速度很快,用户感知不到   真并发 同时发起并发   1 python调用的是操作系统的进程和线程,自身没有    2 一个应用程序默认只有一个进程(可以定义多个) 一个进程只有一个线程(可以定义多个)二 线程   1 python的多线程分为主线程和其他线程.主进程会在多个线程进行来回切换处理   2 python的线程是工作的最小单元   3 python的线程共享进程中的所有资源   4 python无法利用多核CPU实现多线程的,因为有锁的存在,同一时间只能

Python基础知识——Windows上使用python

与大多数Unix系统和服务不同,Windows不需要Python本地,因此不预安装一个版本的Python.但是,CPython团队已经为每个版本编译Windows安装程序(MSI软件包)多年. 随着Python的不断发展,一些以前被支持的平台不再受支持(由于缺乏用户或开发人员).检查PEP 11有关所有不受支持的平台的详细信息. 仍然支持Windows CE. 在Cygwin的安装程序提供安装Python解释器,以及(参见Cygwin包的源,维护者的版本) 有关 具有预编译安装程序的平台的详细信