Python核心基础必备(多线程、多进程编程)(Queue,Lock/Rlock,Condition,Semaphore)

简介: 前言一个人活在这个世界上为了什么呢?我觉得是去经历和享受。对于没做过的事情要做一做。每个人在年轻的时候,所做出的的选择是没有对错之分的,所有的选择都是对的,只能说对于所做选择的结果,只是好与更好的差别。每个人都有自己衡量事物的价值观,我们有什么样的认知就会投影出什么样的图像,所以一定要不断超越有限的认知,不断地提升内外的自由度,不要尝试让自己假装看起来很努力,因为结果不会陪你演戏!学习如逆水行舟 不进则退

实战

什么是GIL ( global interpreter lock ): 全局解释锁

Python中的一个线程对应于c语言当中的一个线程;因为python语言在前期为了简单,在进行编程的时候,会在解释器上面加一个非常大的锁;它允许我们一次只有一个线程运行在我们的CPU上。

学习多线程,希望大家能够了解2点:
1、python在多线程中为什么有人会觉得它慢? ---> 字节码 - 使得同一时刻只能有一个线程在一个CPU上面执行字节码
2、是不是多线程真的就很慢?--->(这个要分情况,后面会进行验证)
  • dis库是python(默认的CPython)自带的一个库,可以用来分析字节码
import dis
# 反编译 将函数变成字节码的流程
def tony(t):
    t = t+1
    return t
print(dis.dis(tony))

image.png

gil在某些情况下是可以被释放掉的--> 这个是python内部的策略问题;所以GIL锁会释放。

  • 第一种情况释放:gil会根据执行的字节码行数以及时间片进行释放gil
  • 第二种情况释放:程序会在遇到io操作的时候 主动释放
什么是时间片? 时间片的概念是什么?
  时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。
- 在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。
- 在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。--->{时间片轮回算法}
a = 0
def time():
    '''
    1. thread_001
    2. io操作  没想象中那么严,会主动释放的
    3. thread_002
    :return:
    '''
    global a  # LEGB
    for item in range(1000000):
        a += 1

def test():
   global a
   for item in range(1000000):
       a -= 1
import threading

thread_1 = threading.Thread(target=time)
thread_2 = threading.Thread(target=test)

thread_1.start()
thread_2.start()

thread_1.join()
thread_2.join()
print(a)

threading多线程-守护线程

image.png

class GetDataHtml(threading.Thread):
    def __init__(self,name):
        super().__init__(name=name)
    def run(self):
        print('开始获取数据html的时间')
        time.sleep(2)
        print('获取数据html结束的时间')

class GetDataUrl(threading.Thread):
    def __init__(self,name):
        super().__init__(name=name)
    def run(self):
        print('开始获取数据url的时间')
        time.sleep(2)
        print('获取数据url结束的时间')
if __name__ == '__main__':
    thread_one=GetDataHtml('tony')
    thread_two=GetDataUrl('老师')
    start_time = time.time()
    thread_one.start()
    thread_two.start()
    # print("中间运行时间:{}".format(time.time()-start_time))
    # 阻塞
    thread_one.join()
    thread_two.join()
    print("中间运行时间:{}".format(time.time()-start_time))

线程间的通讯-共享变量

queue

线程间的通讯

共享变量

import time
import threading

data_list = []
def get_data_html():
    global data_list
    for url in data_list:
        print('开始获取数据html的时间')
        time.sleep(2)
        print('获取数据html结束的时间')
def get_data_url():
    global data_list
    print('开始获取数据url的时间')
    time.sleep(3)
    for item in range(30):
        data_list.append('http://logic.org/{}id'.format(item))
    print('获取数据url结束的时间')
if __name__ == '__main__':
    thread_url = threading.Thread(target=get_data_url)
    thread_1 = threading.Thread(target=get_data_html)
    thread_2 = threading.Thread(target=get_data_html)
    thread_3 = threading.Thread(target=get_data_html)
    thread_4 = threading.Thread(target=get_data_html)
    thread_5 = threading.Thread(target=get_data_html)
    start_time = time.time()
    thread_url.start()
    thread_1.start()
    thread_2.start()
    thread_3.start()
    thread_4.start()
    thread_5.start()
import time
import threading
# from '第七讲-Tony老师' import test_tool

data_list = []
def get_data_html(data_list):
    # global data_list
    while True:
        if len(data_list):
            url=data_list.pop()
            # for url in data_list:
            print('开始获取数据html的时间')
            time.sleep(2)
            print('获取数据html结束的时间')
def get_data_url(data_list):
    # global data_list
    while True:
        print('开始获取数据url的时间')
        time.sleep(3)
        for item in range(30):
            data_list.append('http://logic.org/{id}'.format(id=item))
        print('获取数据url结束的时间')
if __name__ == '__main__':
    thread_url = threading.Thread(target=get_data_url,args=(data_list,))
    for item in range(10):
        thread_html = threading.Thread(target=get_data_html,args=(data_list,))
        thread_html.start()
    start_time = time.time()
    thread_url.start()
    print("中间运行时间:{}".format(time.time()-start_time))

Queue

1.共享变量

2.queue队列 - 它本身是安全的 - 引用了 deque 双端队列

import time
import threading
from queue import Queue

def get_data_html(queue):
    # global data_list
    while True:
        url = queue.get()
        print('开始获取数据html的时间')
        time.sleep(2)
        print('获取数据html结束的时间')


def get_data_url(queue):
    # global data_list
    while True:
        print('开始获取数据url的时间')
        time.sleep(3)
        for item in range(30):
            # 添加
            queue.put('http://logic.org/{id}'.format(id=item))
        print('获取数据url结束的时间')


if __name__ == '__main__':
    # 设置队列
    data_url_queue = Queue(maxsize=1000)
    thread_url = threading.Thread(target=get_data_url,args=(data_url_queue,))
    for item in range(10):
        thread_html = threading.Thread(target=get_data_html,args=(data_url_queue,))
        thread_html.start()
    start_time = time.time()
    # 成对出现的
    data_url_queue.join()
    data_url_queue.task_done()
    # thread_url.start()
    print("中间运行时间:{}".format(time.time()-start_time))

Lock,Rlock

1.线程间的通讯问题

2.线程同步问题 - 为了解决结果不一致

'''

'''
import dis


def time(a):
    a += 1


def test(a):
    a -= 1
'''
0 LOAD_FAST                0 (a)
2 LOAD_CONST               1 (1)
4 INPLACE_ADD   +          1
6 STORE_FAST               0 (a)
'''

print(dis.dis(time))
print(dis.dis(test))

Module_thread_lock.py

'''
A(a,b)
acquire(a)
acquire(b)
                    资源竞争 - 互相等待
B(a,b)
acquire(b)
acquire(a)
'''

from threading import Lock
a = 0
lock = Lock()
def time():
    global a
    global lock
    for item in range(1000000):
        # 上锁
        lock.acquire()
        a += 1
        # 释放锁
        lock.release()


def test():
    global a
    global lock
    for item in range(1000000):
        # 上锁
        lock.acquire()
        a -= 1
        # 释放锁
        lock.release()


import threading

thread_1 = threading.Thread(target=time)
thread_2 = threading.Thread(target=test)

thread_1.start()
thread_2.start()

thread_1.join()
thread_1.join()
print(a)

结论:

1.用锁会影响性能

2.锁会引起死锁 资源竞争 - 互相等待

RLock - 可重入锁

'''

# '''
# import dis
#
#
# def time(a):
#     a += 1
#
#
# def test(a):
#     a -= 1
# '''
# 0 LOAD_FAST                0 (a)
# 2 LOAD_CONST               1 (1)
# 4 INPLACE_ADD   +          1
# 6 STORE_FAST               0 (a)
# '''
#
# print(dis.dis(time))
# print(dis.dis(test))
'''
A(a,b)
acquire(a)
acquire(b)
                    资源竞争 - 互相等待
B(a,b)
acquire(b)
acquire(a)
'''
from threading import Lock, RLock
a = 0
lock = RLock()
def time():
    global a
    global lock
    for item in range(1000000):
        # 上锁
        lock.acquire()
        # do_time(lock)
        lock.acquire()
        a += 1
        # 释放锁
        lock.release()
        lock.release()


def do_time(lock):
    # 上锁
    lock.acquire()
    # 0---------------------
    # 释放锁
    lock.release()


def test():
    global a
    global lock
    for item in range(1000000):
        # 上锁
        lock.acquire()
        a -= 1
        # 释放锁
        lock.release()

import threading

thread_1 = threading.Thread(target=time)
thread_2 = threading.Thread(target=test)

thread_1.start()
thread_2.start()

thread_1.join()
thread_1.join()
print(a)

Condition(条件变量):主要用于复杂线程间同步的一个锁。 ---> 通过condition完成双方协同聊天的功能

wait 和 notify 是condition的精髓。
在调用with cond 之后才能调用 wait 或者 notify方法。 PS: 必须要在with语句下面,才能够成功;否则会报错。

在condition中 有2层锁, 一把底层锁会在线程调用wait方法的时候释放,上面的锁会在每次调用wait的时候分配一把并放入cond中的等待队列中;直到notify方法的唤醒。

import threading

from threading import Condition

'''
张三 : 你好,很高兴认识你     A
马六 : 我也是                B
张三 : 你是什么样的人      
马六 : 我是憨憨
张三 : 。。。
马六 : 。。。。。。
'''


class ZhangSan(threading.Thread):
    def __init__(self, lock):
        super().__init__(name='张三')
        self.lock = lock

    def run(self):
        lock.acquire()
        print("{} : 你好,很高兴认识你".format(self.name))
        lock.release()

        # lock.acquire()
        # print("{} : 你是什么样的人".format(self.name))
        # lock.release()


class MaLiu(threading.Thread):
    def __init__(self, lock):
        self.lock = lock
        super().__init__(name='马六')

    def run(self):
        lock.acquire()
        print("{} : 我也是".format(self.name))
        lock.release()

        # lock.acquire()
        # print("{} : 我是憨憨".format(self.name))
        # lock.release()

if __name__ == '__main__':
    lock = threading.Lock()
    zhang_san = ZhangSan(lock)
    ma_liu = MaLiu(lock)

    zhang_san.start()
    ma_liu.start()

Condition

import threading

from threading import Condition

'''
张三 : 你好,很高兴认识你     A
马六 : 我也是                B
张三 : 你是什么样的人      
马六 : 我是憨憨
张三 : 。。。
马六 : 。。。。。。
'''


class ZhangSan(threading.Thread):
    def __init__(self, cond):
        super().__init__(name='张三')
        self.cond = cond

    def run(self):
        with self.cond:
            # self.cond.acquire()
            print("{} : 你好,很高兴认识你".format(self.name))
            self.cond.notify()
            self.cond.wait()
    
            print("{} : 你是什么样的人".format(self.name))
            self.cond.notify()
            self.cond.wait()
            # self.cond.release()


class MaLiu(threading.Thread):
    def __init__(self, cond):
        self.cond = cond
        super().__init__(name='马六')

    def run(self):
        with self.cond:
            # self.cond.acquire()
            self.cond.wait()
            print("{} : 我也是".format(self.name))
            self.cond.notify()
    
            self.cond.wait()
            print("{} : 我是憨憨".format(self.name))
            self.cond.notify()
            # self.cond.release()

if __name__ == '__main__':
    cond = threading.Condition()
    zhang_san = ZhangSan(cond)
    ma_liu = MaLiu(cond)
    # 程序的执行顺序
    ma_liu.start()
    zhang_san.start()

Semaphore (用于控制进入数量的锁)

我们的线程对于操作系统来说,如果线程越多,我们操作系统的切换就会越慢;所以在某些时候我们想要控制线程并发数量的时候,Semaphore的意义是非常重要的;

'''
读和写
Semaphroe():用于控制进入的数量的锁, 信号量
'''
import threading
# threading.Semaphore()
import time


class GetUrl(threading.Thread):
    def __init__(self, sem):
        super().__init__()
        self.sem = sem

    def run(self):
        # list.append()
        for item in range(20):
            # 上锁
            self.sem.acquire()
            html_thread = HtmlSpider('http://news.baidu.com/{}'.format(item), self.sem)
            html_thread.start()


class HtmlSpider(threading.Thread):
    def __init__(self, url, sem):
        super().__init__()
        self.url = url
        self.sem = sem

    def run(self):
        time.sleep(2)
        print("获取了一页html详情信息!")
        # 释放锁
        self.sem.release()


if __name__ == '__main__':
    # 只允许并发3个
    sem = threading.Semaphore(3)
    url_threads = GetUrl(sem)
    url_threads.start()

在这个浮躁的时代;竟然还有人能坚持篇篇原创;

如果本文对你学习有所帮助-可以点赞👍+ 关注!将持续更新更多新的文章。

支持原创。感谢!

相关文章
|
4天前
|
Python
Python编程中的异常处理:理解与实践
【9月更文挑战第14天】在编码的世界里,错误是不可避免的。它们就像路上的绊脚石,让我们的程序跌跌撞撞。但是,如果我们能够预见并优雅地处理这些错误,我们的程序就能像芭蕾舞者一样,即使在跌倒的边缘,也能轻盈地起舞。本文将带你深入了解Python中的异常处理机制,让你的代码在面对意外时,依然能保持优雅和从容。
139 73
|
4天前
|
人工智能 数据挖掘 数据处理
揭秘Python编程之美:从基础到进阶的代码实践之旅
【9月更文挑战第14天】本文将带领读者深入探索Python编程语言的魅力所在。通过简明扼要的示例,我们将揭示Python如何简化复杂问题,提升编程效率。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往高效编码世界的大门。让我们开始这段充满智慧和乐趣的Python编程之旅吧!
|
3天前
|
数据采集 机器学习/深度学习 人工智能
Python编程入门:从零基础到实战应用
【9月更文挑战第15天】本文将引导读者从零开始学习Python编程,通过简单易懂的语言和实例,帮助初学者掌握Python的基本语法和常用库,最终实现一个简单的实战项目。文章结构清晰,分为基础知识、进阶技巧和实战应用三个部分,逐步深入,让读者在学习过程中不断积累经验,提高编程能力。
|
4天前
|
机器学习/深度学习 数据采集 人工智能
探索Python的奥秘:从基础到进阶的编程之旅
在这篇文章中,我们将深入探讨Python编程的基础知识和进阶技巧。通过清晰的解释和实用的示例,无论您是编程新手还是有经验的开发者,都能从中获得有价值的见解。我们将覆盖从变量、数据类型到类和对象的各个方面,助您在编程世界里游刃有余。
21 10
|
2天前
|
存储 机器学习/深度学习 数据挖掘
深入浅出:Python编程入门与实践
【9月更文挑战第16天】本文以“深入浅出”的方式,引领读者步入Python编程的世界。从基础语法到实际应用,我们将一步步探索Python的魅力所在。无论你是编程新手,还是希望拓展技能的老手,这篇文章都将为你提供有价值的信息和指导。通过本文的学习,你将能够编写出简单而实用的Python程序,为进一步深入学习打下坚实的基础。让我们一起开始这段编程之旅吧!
|
2天前
|
存储 Python 容器
Python编程基础第二天学习笔记
Python编程的第二天学习是建立在基础概念上的深化和扩展,强调了基本语法、数据类型、控制结构和函数的重要性。通过实践这些概念,可以增强对Python编程语言的理解,并为后续的高级学习打下坚实的基础。继续实践并逐渐探索更复杂的编程任务将有助于巩固和扩展这些基础知识。
21 7
|
2天前
|
机器学习/深度学习 存储 人工智能
探索Python编程的魔法:从基础到进阶
【9月更文挑战第16天】本文将带领你进入Python编程的世界,无论你是初学者还是有一定经验的开发者。我们将一起揭开Python编程的神秘面纱,通过实际案例和代码示例,深入浅出地探讨Python的基础语法、数据结构、面向对象编程以及函数式编程等核心概念。文章旨在提供一条清晰的学习路径,帮助你构建坚实的编程基础,并逐步过渡到更高级的编程技巧。无论你的目标是数据分析、网络开发还是机器学习,这篇文章都将为你打下扎实的基础,让你在编程的道路上越走越远。
15 6
|
2天前
|
机器学习/深度学习 数据挖掘 程序员
Python编程基础:从入门到实践
【9月更文挑战第16天】本文是一篇Python编程的入门教程,旨在帮助初学者理解Python的基本概念和语法。文章首先介绍了Python的历史和特点,然后详细讲解了Python的基本语法,包括变量、数据类型、运算符、控制结构等。接着,文章通过一些实例代码,展示了如何使用Python进行基本的编程操作,如输入输出、条件判断、循环等。最后,文章还提供了一些学习资源和建议,帮助读者进一步学习和掌握Python编程。
|
7天前
|
消息中间件 安全 Kafka
Python IPC机制全攻略:让进程间通信变得像呼吸一样自然
【9月更文挑战第12天】在编程领域,进程间通信(IPC)是连接独立执行单元的关键技术。Python凭借简洁的语法和丰富的库支持,提供了多种IPC方案。本文将对比探讨Python的IPC机制,包括管道与消息队列、套接字与共享内存。管道适用于简单场景,而消息队列更灵活,适合高并发环境。套接字广泛用于网络通信,共享内存则在本地高效传输数据。通过示例代码展示`multiprocessing.Queue`的使用,帮助读者理解IPC的实际应用。希望本文能让你更熟练地选择和运用IPC机制。
30 10
|
4天前
|
开发者 Python
深入浅出操作系统:进程与线程的奥秘
【8月更文挑战第46天】在数字世界的幕后,操作系统扮演着至关重要的角色。本文将揭开进程与线程这两个核心概念的神秘面纱,通过生动的比喻和实际代码示例,带领读者理解它们的定义、区别以及如何在编程中运用这些知识来优化软件的性能。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和实用技巧。