首页 > 编程语言 >进入python的世界_day35_网络编程——线程相关知识、GIL锁,互斥锁、event事件、池子、协程

进入python的世界_day35_网络编程——线程相关知识、GIL锁,互斥锁、event事件、池子、协程

时间:2022-11-21 20:22:38浏览次数:43  
标签:__ 协程 day35 互斥 线程 time print import 进程

上周五作业——多进程实现TCP服务端并发

# 服务端
import socket
from multiprocessing import Process


def get_server():
    server = socket.socket()
    server.bind(('127.0.0.1', 9999))
    server.listen(5)
    return server  # 把封装好属性的socket对象传出去给其他函数使用


def get_talk(sock):
    while True:
        data = sock.recv(1024)
        print(data.decode('utf8'))
        sock.send(data.upper())


if __name__ == '__main__':
    server = get_server()  # 拿到socket对象
    while True:
        sock, addr = server.accept()  # 建立通道,拿到客户端地址
        # 开设多进程去聊天
        p = Process(target=get_talk, args=(sock,))
        p.start()
 _______________________________
# 客户端没差别,老样子写普通版本的

一、互斥锁——Lock、mutex

将并发、并行变为串行,牺牲了效率但是提升了数据安全

from multiprocessing import Process,Lock

造锁
mutex = Lock()

用锁

mutex.acquire()  # 抢锁,得放在操作数据层
mutex.release()  # 放锁

二、线程理论

​ 进程——资源单位

​ 线程——执行单位

​ 一个进程内部至少有一个线程!!!

​ 进程是开辟内存空间,线程是在进程上执行不占内存空间,所以创建进程的资源占用是比创建进程大的多的

from threading import Thread
import time


def a(name):
    time.sleep(0.01)  # 因为线程执行的飞快,加一个阻塞试试
    print(f'{name} 正在运行')
    time.sleep(3)
    print(f'{name} 执行完毕')


if __name__ == '__main__':
    t = Thread(target=a, args=('jack',))
    t.start()
    print('主线程执行了')
    
>>>
主线程执行了
jack 正在运行
jack 执行完毕

三、线程的诸多方法

1.join方法

​ 一样的,和进程

2.同一个进程下的多个线程数据共享

​ 因为不是多个复制内存空间,所以是共用的数据

3.线程名字

​ 类似进程ID一样的,current_thread

4.线程进程号

​ 同一个进程下的多个线程都是一个进程号

5.统计同一个进程下的线程数

​ activeCount

​ 注意如果没手动添加阻塞,用for循环生成多个线程,每循环生成一个执行完的线程就消失了

6.守护线程

​ 和守护进程一样

7.互斥锁

​ 也有Lock子模块,用法一样

四、GIL全局解释器锁——只在CPython有

1.介绍

​ 1.在CPython解释器中存在全局解释器锁简称GIL
​ python解释器有很多类型
​ CPython JPython PyPython (常用的是CPython解释器)

​ 2.GIL本质也是一把互斥锁 用来阻止同一个进程内多个线程同时执行(重要)

​ 3.GIL的存在是因为CPython解释器中内存管理不是线程安全的(垃圾回收机制)

2.如何绕过GIL

from threading import Thread
import time

num = 1


def a():
    global num
    c = num
    time.sleep(0.5)  # 其实这里和抢票差不多,因为是多线程,让线程都睡一会,又重新弄了一个变量名,强行避开了GIL锁
    num = c + 1


t_list = []
for i in range(1, 100):
    t = Thread(target=a)
    t.start()
    t_list.append(t)   
for t in t_list:   # 确保所有的进程全部运行完毕
    t.join()
print(num)
>>>
2   
# 如果不在a中新弄变量名,GIL锁会影响,得到结果为100

​ GIL 只能确保同进程内多线程数据不会被垃圾回收机制弄乱,不能确保程序里面的数据安全

验证python多线程 是否有用

需要分情况

1.cpu情况

​ 单个cpu
​ 多个cpu

2.代码情况

​ io密集型(代码有io操作)
​ 计算密集型(代码没有io)

3.单个cpu

  • io密集型

​ 1.多进程

​ 总耗时(单进程的耗时+io+申请空间+拷贝代码)

​ 2.多线程

​ 总耗时(单进程的耗时加+io)

​ !!!多线程有优势

  • 计算密集型

​ 1.多进程

​ 申请额外的空间 消耗更多的资源(总耗时+申请空间+拷贝代码+切换)

​ 2.多线程

​ 耗时资源相对较少 通过多道技术(总耗时+切换)

​ !!!多线程有优势

4.多个cpu

  • io密集型

​ 1.多进程

​ 总耗时(单个进程的耗时+io+申请空间+拷贝代码)

​ 2.多线程

​ 总耗时(单进程耗时+io)

​ !!!多线程有优势

  • 计算密集型

​ 1.多进程

​ 总耗时(单个进程的耗时)

​ 2.多线程

​ 总耗时(多个进程的综合)

​ !!!多进程完胜!

五、信号量

在python并发编程中信号量相当于多把互斥锁(打个比方公共厕所)

在其他的知识中意思达到某个条件自动触发其他操作

from threading import Thread, Lock, Semaphore
import time
import random


sp = Semaphore(5)  # 一次性产生五把锁


class MyThread(Thread):
    def run(self):
        sp.acquire()
        print(self.name)
        time.sleep(random.randint(1, 3))
        sp.release()


for i in range(20):
    t = MyThread()
    t.start()
    
# 相当于让20个人去挤厕所,厕所只有五个位置,哪个位置有人出来就可以新的人进去上

六、event事件——可以暂停线程或者进程

from threading import Thread, Event
import time

event = Event()  # 造一个event对象


def light():
    print('红路灯红灯亮着,等待绿灯>>>')
    time.sleep(1)
    print('3')
    time.sleep(1)
    print('2')
    time.sleep(1)
    print('1')
    time.sleep(1)
    print('绿灯亮了!游戏开始!')
    event.set()  # 让被等待的对象立马执行


def car(name):
    print(f'{name} 正在起跑线等红绿灯')
    event.wait()  # 让根据这个函数产生的进程等着
    print(f'游戏开始! {name}冲刺! ')


t = Thread(target=light)
t.start() # 这个函数产生的进程睡期间下面的代码执行,立马生成进程

for i in range(1, 10):
    t = Thread(target=car, args=(f'棉花糖SR{i}',))
    t.start()
    
>>>

红路灯红灯亮着,等待绿灯>>>
棉花糖SR1 正在起跑线等红绿灯
棉花糖SR2 正在起跑线等红绿灯
棉花糖SR3 正在起跑线等红绿灯
棉花糖SR4 正在起跑线等红绿灯
棉花糖SR5 正在起跑线等红绿灯
棉花糖SR6 正在起跑线等红绿灯
棉花糖SR7 正在起跑线等红绿灯
棉花糖SR8 正在起跑线等红绿灯
棉花糖SR9 正在起跑线等红绿灯
3
2
1
绿灯亮了!游戏开始!
游戏开始! 棉花糖SR4冲刺! 
游戏开始! 棉花糖SR5冲刺! 
游戏开始! 棉花糖SR2冲刺! 
游戏开始! 棉花糖SR1冲刺! 
游戏开始! 棉花糖SR8冲刺! 
游戏开始! 棉花糖SR6冲刺! 
游戏开始! 棉花糖SR3冲刺! 
游戏开始! 棉花糖SR7冲刺! 
游戏开始! 棉花糖SR9冲刺!

七、进程池、线程池

​ 能提前设置好最大进程、线程数量,可以有预防性的防止电脑因为进程或线程太多卡死

​ 当然,因为做出了限制,所以程序的执行效率是降低了

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time

# 1产生固定数量的线程值
p = ProcessPoolExecutor(12)
# pool = ThreadPoolExecutor(10)

def test(name):
    print(f'测试正在执行 测试员{name}>>>')
    time.sleep(2)
    print(f'测试执行完毕 测试员{name}>>>')
    return '工作完成'


def func(a):
    print('我来负责收结果')
    print(a.result()) # 把返回值打印出来,得用result()


if __name__ == '__main__': # 进程别忘了main

    for i in range(1, 24):
        # 直接往池子里塞任务,池子按照设置好的数量分批创建进程
        p.submit(test, 'jack').add_done_callback(func) # 通过调专门接收结果的函数来获取进程执行完的结果

八、协程——gevent第三方模块

​ 因为CPU才是干活的核心,而程序执行过程中如果碰到io操作,CPU会暂时离开,协程技术能欺骗CPU伪装代码中没有io操作,能让CPU一直陪同跟随,达到最大效率的目的。

​ 这个模块不需要去了解底层原理,这是别的大佬封装好的方法,会用就行

from gevent import monkey;
monkey.patch_all()  # 固定语法,用于检测所有的IO操作(猴子补丁)
from gevent import spawn
import time


def a():
    print('a 正在运行')
    time.sleep(2)
    print('a 执行结束')


def b():
    print('b 正在运行')
    time.sleep(3)
    print('b 执行结束')


if __name__ == '__main__':
    start_time = time.time()
    a()
    b()
    print(time.time() - start_time)# >>> 5.056353569030762 
    
————————————————————————————————————————————————————————
if __name__ == '__main__':
    start_time = time.time()
    s1 = spawn(a)
    s2 = spawn(b)
    s1.join()
    s2.join()
    print(time.time() - start_time) # >>>3.0264976024627686
    

九、单线程实现高并发

# 服务端
from gevent import monkey;
monkey.patch_all()
from gevent import spawn
import socket

def a(sock):
    while True:
        date = sock.recv(1024)
        print(date.decode('utf8'))
        sock.send(date.upper())


def s():
    sever = socket.socket()
    sever.bind(('127.0.0.1', 8088))
    sever.listen(5)
    while True:
        sock, addr = sever.accept()
        spawn(a, sock)


s1 = spawn(s)
s1.join()

————————————————————————————————————————————————————
# 客户端
import socket

c1 = socket.socket()
c1.connect(('127.0.0.1', 8088))

while True:
    c1.send(b'hello world')
    date = c1.recv(1024)
    print(date.decode('utf8'))
# 记得去开pycharm中单一个Py文件的重复执行


↑↑↑↑↑↑↑↑

标签:__,协程,day35,互斥,线程,time,print,import,进程
From: https://www.cnblogs.com/wznn125ml/p/16913067.html

相关文章

  • 互斥锁、死锁及GIL全局解释器锁
    互斥锁、死锁及GIL全局解释器锁互斥锁在生产者消费者模型中,我们需要一个消息队列、文件、数据库来充当我们的缓冲区完成进程间的通信,而进程同时处理数据是存在不安全性的......
  • 互斥锁、线程
    今日内容概要多进程实现TCP服务端并发互斥锁代码实操线程理论创建线程的多种方式线程join方法守护线程线程诸多方法GIL全局解释器锁今日内容详情多进程实......
  • 多进程实现TCP服务端并发、互斥锁、线程及代码实现、GIL全局解释器锁、信号量、event
    目录一、多进程实现TCP服务端并发二、互斥锁代码实操1、互斥锁的概念2、互斥锁的使用3、死锁现象4、小结三、线程理论进程线程线程简介为什么要使用多线程?多线程概念多进......
  • 多线程、GIL全局解释器锁、协程
    目录多进程实现TCP服务端并发互斥锁代码实操线程理论创建线程的两种方式线程的诸多特性GIL全局解释器锁验证GIL的存在GIL与普通互斥锁python多线程是否有用死锁现象信号量e......
  • 一起聊聊 Kotlin 协程
    正文大家好,我是朱涛,最近刚刚通过Google的认证,成为了Android以及Kotlin领域的谷歌开发者专家(GoogleDeveloperExpert)。这周我在GDG(GoogleDeveloperGroup)和字......
  • Golang中协程调度器底层实现( G、M、P)
    三个必知的核心元素。(G、M、P)G:Goroutine的缩写,一个G代表了对一段需要被执行的Go语言代码的封装M:Machine的缩写,一个M代表了一个内核线程,等同于系统线程P:Processor的缩写......
  • python的协程
    python协程库gevent学习--gevent数据结构及实战(三)gevent学习系列第三章,前面两章分析了大量常用几个函数的源码以及实现原理。这一章重点偏向实战了,按照官方给出的ge......
  • kotlin的协程
    一、关于协程库的导入1、使用下载包的方式可以到该网站下载https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core对应的协程库本地调试......
  • 互斥锁
    互斥锁理论'''多个线程同时要修改同一份数据,类似抢票等,都需要用到互斥锁虽然影响效率,但保证数据安全'''importjsonfrommultiprocessingimportProcessimport......
  • python 协程学习笔记
    yield生成器frominspectimportgetgeneratorstatedefgen1():x=yield2print(x)y=yieldxreturnyg=gen1()print(getgeneratorstate(......