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

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

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

支持原创。感谢!

相关文章
|
1天前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
5天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
12天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
14天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
11天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
14天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
24 1
|
14天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
15天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
42 1
|
Java Python
Python基础 | 深浅拷贝问题、递归函数练习
在实际工作中,经常涉及到数据的传递,在数据传递使用过程中,可能会发生数据被修改的问题。为了防止数据被修改,就需要在传递一个副本,即使副本被修改,也不会影响原数据的使用。为了生成这个副本,就产生了拷贝。下面先了解一下几个概念:对象、可变类型、引用
255 0
Python基础 | 深浅拷贝问题、递归函数练习
|
Python
python——基础练习(五)
python——基础练习(五)
134 0
python——基础练习(五)
下一篇
无影云桌面