百万年薪python之路 -- 并发编程之 多线程 一

多线程

1.进程: 生产者消费者模型

一种编程思想,模型,设计模式,理论等等,都是交给你一种编程的方法,以后遇到类似的情况,套用即可

生产者与消费者模型的三要素:
  1. 生产者:产生数据的
  2. 消费者:接收数据做进一步处理的
  3. 容器: 缓存区(队列) 起到缓冲的作用,平衡生产力与消费者,解耦

2.线程的理论知识

  1. 什么是线程

    一条流水线的工作流程

    进程: 在内存中开启一个进程空间,然后将主进程的所有的资源复制一份,然后调用cpu去执行这些代码.

    进程是资源调度的基本单位,而线程是cpu的最小执行单位

    进程的开启: 进程会在内存中开辟一个进程空间,将主进程的数据全部复制一份,线程会执行里面的代码

  2. 线程vs进程
    1. 开启进程的开销非常大,比线程的开销大很多.
    2. 开启线程的速度非常快,要比进程快几十倍到上百倍.
    3. 同一个进程内,线程与线程之间可以共享数据, 进程与进程之间需要借助队列等方法实现通信.
  3. 线程的应用

    并发: 一个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进程的代码对比

  1. 开启速度对比

    # 多进程
    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==') # 线程是没有主次之分
  2. 对比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()}")
  3. 同一个进程内线程共享内部数据
    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}')

原文地址:https://www.cnblogs.com/zhangchaoyin/p/11415382.html

时间: 2024-11-05 16:26:36

百万年薪python之路 -- 并发编程之 多线程 一的相关文章

百万年薪python之路 -- 并发编程之 多线程 二

1. 死锁现象与递归锁 进程也有死锁与递归锁,进程的死锁和递归锁与线程的死锁递归锁同理. 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因为争夺资源而造成的一种互相等待的现象,在无外力的作用下,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在相互等待的进程称为死锁进程 # 多个线程多个锁可能会产生死锁 from threading import Thread from threading import Lock import time lock_A = Lock

百万年薪python之路 -- 并发编程之 多进程二

1. 僵尸进程和孤儿进程 基于unix的环境(linux,macOS) 主进程需要等待子进程结束之后,主进程才结束 主进程时刻检测子进程的运行状态,当子进程结束之后,一段时间之内,将子进程进行回收. 为什么主进程不在子进程结束后马上对其回收呢? 主进程与子进程是异步关系,主进程无法马上捕获子进程什么时候结束 如果子进程结束之后马上在内存中释放资源,主进程就没有办法检测子进程的状态. Unix针对于上面的问题提供了一个机制 所有的子进程结束之后,立马会释放掉文件的操作链接和内存的大部分数据,但是会

python之路 -- 并发编程之线程

进程 是 最小的内存分配单位 线程 是 操作系统调度的最小单位 线程直接被CPU执行,进程内至少含有一个线程,也可以开启多个线程 开启一个线程所需要的时间要远远小于开启一个进程 GIL锁(即全局解释器锁) 锁的是线程 在Cpython解释器下的python程序 在同一时刻 多个线程中只能有一个线程被CPU执行 1.创建线程的两中方式: import time from threading import Thread def func(args): time.sleep(1) print(args

百万年薪python之路 -- 模块

1.自定义模块 1.1.1 模块是什么? 模块就是文件,存放一堆常用的函数和变量的程序文件(.py)文件 1.1.2 为什么要使用模块? 1.避免写重复代码,从文件级别组织程序,更方便管理 2.可以多次利用,我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用 3.拿来主义,提升开发效率 同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率,避免重复造轮子. 1.1.3 模块的分类 Pyt

百万年薪python之路 -- 模块二

1. 序列化模块 什么是序列化呢? 序列化的本质就是将一种数据结构(如字典.列表)等转换成一个特殊的序列(字符串或者bytes)的过程就叫做序列化. 为什么要有序列化模块? 如果你写入文件中的字符串是一个序列化后的特殊的字符串,那么当你从文件中读取出来,是可以转化回原数据结构的. 作用及用途 序列化模块就是将一个常见的数据结构转化成一个特殊的序列,并且这个特殊的序列还可以反解回去.它的主要用途:文件读写数据,网络传输数据. 1.1 json序列化(很重要) 不同语言都遵循json数据转化格式,即

百万年薪python之路 -- MySQL数据库之 常用数据类型

MySQL常用数据类型 一. 常用数据类型概览 # 1. 数字: 整型: tinyint int bigint 小数: float: 在位数比较短的情况下不精确 double: 在位数比较长的情况下不精确 0.000001230123123123 存成: 0.000001230000 decimal: (如果用小数,则推荐使用decimal) 精准 内部原理是以字符串形式去存 # 2. 字符串: char(10): 简单粗暴,浪费空间,存取速度快. root存成root000000 varcha

百万年薪python之路 -- MySQL数据库之 存储引擎

MySQL之存储引擎 一. 存储引擎概述 定义: 存储引擎是mysql数据库独有的存储数据.为数据建立索引.更新数据.查询数据等技术的实现方法 ? 首先声明一点: 存储引擎这个概念只有MySQL才有. ? 在讲清楚什么是存储引擎之前,我们先来个比喻,我们都知道录制一个视频文件,可以转换成不同的格式,例如mp4,avi,wmv等,而存在我们电脑的磁盘上也会存在于不同类型的文件系统中如windows里常见的ntfs.fat32,存在于linux里常见的ext3,ext4,xfs,但是,给我们或者用户

百万年薪python之路 -- MySQL数据库之 MySQL行(记录)的操作(一)

MySQL的行(记录)的操作(一) 1. 增(insert) insert into 表名 value((字段1,字段2...); # 只能增加一行记录 insert into 表名 values(字段1,字段2...); insert into 表名(id,name) values(字段1,字段2),(xx1,xx2); id,name,age 插入查询结果 语法: INSERT INTO 表名(字段1,字段2,字段3-字段n) SELECT (字段1,字段2,字段3-字段n) FROM 表2

百万年薪python之路 -- JS基础介绍及数据类型

JS代码的引入 方式1: <script> alert('兽人永不为奴!') </script> 方式2:外部文件引入 src属性值为js文件路径 <script src="test.js"></script> 变量声明 变量名是区分大小写的. 推荐使用驼峰式命名规则.首字母大写 保留字不能用做变量名. 声明前要加var关键字. var a = 1; 加var定义的变量是全局变量,在函数里定义会是局部变量 不加定义的变量不管在哪,都是全局