多线程

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}')

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

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

    1. 死锁现象与递归锁 进程也有死锁与递归锁,进程的死锁和递归锁与线程的死锁递归锁同理. 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因为争夺资源而造成的一种互相等待的现象,在无外力的作用 ...

  2. 百万年薪python之路 -- 并发编程之 多线程 三

    1. 阻塞,非阻塞,同步,异步 进程运行的三个状态: 运行,就绪,阻塞. 从执行的角度: ​ 阻塞: 进程运行时,遇到IO了,进程挂起,CPU被切走. ​ 非阻塞: 进程没有遇到IO 当进程遇到IO, ...

  3. 百万年薪python之路 -- 并发编程之 多进程 一

    并发编程之 多进程 一. multiprocessing模块介绍 ​ python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大 ...

  4. 百万年薪python之路 -- 并发编程之 协程

    协程 一. 协程的引入 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两 ...

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

    1. 僵尸进程和孤儿进程 基于unix的环境(linux,macOS) 主进程需要等待子进程结束之后,主进程才结束 主进程时刻检测子进程的运行状态,当子进程结束之后,一段时间之内,将子进程进行回收. ...

  6. 百万年薪python之路 -- 数据库初始

    一. 数据库初始 1. 为什么要有数据库? ​ 先来一个场景: ​ 假设现在你已经是某大型互联网公司的高级程序员,让你写一个火车票购票系统,来hold住十一期间全国的购票需求,你怎么写? 由于在同一时 ...

  7. 百万年薪python之路 -- Socket

    Socket 1. 为什么学习socket 你自己现在完全可以写一些小程序了,但是前面的学习和练习,我们写的代码都是在自己的电脑上运行的,虽然我们学过了模块引入,文件引入import等等,我可以在程序 ...

  8. 百万年薪python之路 -- 面向对象之三大特性

    1.面向对象之三大特性 1.1封装 封装:就是把一堆代码和数据,放在一个空间,并且可以使用 对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封 ...

  9. 百万年薪python之路 -- 面向对象之继承

    面向对象之继承 1.什么是面向对象的继承 继承(英语:inheritance)是面向对象软件技术当中的一个概念. 通俗易懂的理解是:子承父业,合法继承家产 专业的理解是:子类可以完全使用父类的方法和属 ...

随机推荐

  1. [Leetcode][动态规划] 第931题 下降路径最小和

    一.题目描述 给定一个方形整数数组 A,我们想要得到通过 A 的下降路径的最小和. 下降路径可以从第一行中的任何元素开始,并从每一行中选择一个元素.在下一行选择的元素和当前行所选元素最多相隔一列. 示 ...

  2. jenkins离线安装插件的方法(无法访问外网)

    最近项目要迁移环境,无法访问外网,因此jenkins的安装配置需要离线操作,在此记录 jenkins下载安装好之后,跳过插件的安装,新建用户进入jenkins界面,这些前置步骤我在之前的随笔里有写具体 ...

  3. .netCore+Vue 搭建的简捷开发框架 (4)--NetCore 基础

    书接上文:上一节中,我们已经实现Services 层.(https://www.cnblogs.com/xuzhencheng/p/11424751.html) 但是具体要如何将服务依赖注入进来呢?继 ...

  4. 痞子衡嵌入式:飞思卡尔i.MX RTyyyy系列MCU特性那些事(5)- 划时代新品RT1170

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RTyyyy系列MCU的划时代新品i.MXRT1170. 自2017年开始,每年的6月25日恩智浦都会在北京举行微控制 ...

  5. mysql操作遇到的坑(第二版)

    1.通过条件查询出上一条与下一条 sql说明:本表关联本表,然后通过其中一个表,查询出对应的条件,再用另外一个表求出上一条与下一条的数据,求出来的数据是多条的 SELECT ua.id, ua.wx_ ...

  6. Nepxion Discovery【探索】微服务企业级解决方案

    Nepxion Discovery[探索]微服务企业级解决方案] Nepxion Discovery[探索]使用指南,基于Spring Cloud Greenwich版.Finchley版和Hoxto ...

  7. Video/audio标签的一些基础使用心得

    常用方法 .play():用于音频视频的播放 .pause():用于音频视频的暂停 常用属性 <audio src="Batmobile Battle Mode Reveal Musi ...

  8. package.json详解

    1.概念 Node.js项目遵循模块化的架构,当我们创建了一个Node.js项目,意味着创建了一个模块,这个模块的描述文件,被称为package.json 亦即:模块的描述文件 = package.j ...

  9. idea tomcat提示Unable to ping server at localhost:1099

    idea启动tomcat报错Unable to ping server at localhost:1099 是 IDEA配置的jdk版本 与 tomcat的jdk版本不配导致的

  10. BT面板安装php报错configure: error: C preprocessor “/lib/cpp” fails sanity check

    使用宝塔面板安装扩展时已经显示添加安装成功了,待我刷新浏览器之后没有安装成功.看了一下执行日志. 缺少必要的C++库,如下命令重装解决. yum reinstall glibc-headers gcc ...