python基础之并发编程(一)

目录
  • 一、进程(Process)
  • 二、线程(Thread)
  • 三、并发编程解决方案:
  • 四、多线程实现 (两种)
    • 1、第一种 函数方法
    • 2、第二种 类方法包装
  • 五、守护线程与子线程
    • 1、线程在分法有:
    • 2、守护线程
  • 六、锁
  • 七、死锁
  • 八、信号量(Semaphore)
  • 九、事件(Event)
  • 十、线程通信-队列
    • 1使用的队列的好处:
    • 2Queue模块中的常用方法:
  • 十一、生产者和消费者模式
  • 总结

一、进程(Process)

是一个具有一定独立功能的程序关于某个数据集合的一次运行活动

二、线程(Thread)

是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进 程中的实际运作单位。

三、并发编程解决方案:

1、多任务的实现有 3 种方式:

  • 多进程模式;
  • 多线程模式;
  • 多进程+多线程模式

四、多线程实现 (两种)

1、第一种 函数方法

# 方法包装-启动多线程
from threading import Thread
from time import sleep, time
def func1(name):
    print("Threading:{} start".format(name))
    sleep(3)
    print("Threading:{} end".format(name))
if __name__ == '__main__':
    # 开始时间
    start = time()
    # 创建线程列表
    t_list = []
    # 循环创建线程
    for i in range(10):
        t = Thread(target=func1, args=('t{}'.format(i),))
        t.start()
        t_list.append(t)
    # 等待线程结束
    for t in t_list:
        t.join()
    # 计算使用时间
    end = time() - start
    print(end)

2、第二种 类方法包装

# 类包装-启动多线程
from threading import Thread
from time import sleep, time
class MyThread(Thread):
    def __init__(self,name):
        Thread.__init__(self)
        self.name =name
    def run(self):
        print("Threading:{} start".format(self.name))
        sleep(3)
        print("Threading:{} end".format(self.name))
if __name__ == '__main__':
    # 开始时间
    start = time()
    # 创建线程列表
    t_list = []
    # 循环创建线程
    for i in range(10):
        t = MyThread('t{}'.format(i))
        t.start()
        t_list.append(t)
    # 等待线程结束
    for t in t_list:
        t.join()
    # 计算使用时间
    end = time() - start
    print(end)

注意:

主线程不会等待子线程运行结束,如果需要等待可使用 join()方法不要启动线程后立即 join(),很容易造成串行运行,导致并发失效

五、守护线程与子线程

1、线程在分法有:

主线程:程序的本身

子线程:在程序另开起的线程

2、守护线程

主要的特征是它的生命周期。主线程死亡,它也就随之 死亡

# 类包装-启动多线程
from threading import Thread
from time import sleep, time
class MyThread(Thread):
    def __init__(self,name):
        Thread.__init__(self)
        self.name =name
    def run(self):
        print("Threading:{} start".format(self.name))
        sleep(3)
        print("Threading:{} end".format(self.name))
if __name__ == '__main__':
    # 开始时间
    start = time()
    # 循环创建线程
    for i in range(10):
        t = MyThread('t{}'.format(i))
        t.setDaemon(True)
        t.start()
    # 计算使用时间
    end = time() - start
    print(end)

六、锁

from threading import Thread
def func1(name):
    print('Threading:{} start'.format(name))
    global num
    for i in range(50000000): # 有问题
    #for i in range(5000): # 无问题
        num += 1
    print('Threading:{} end num={}'.format(name, num))
if __name__ == '__main__':
    num =0
    # 创建线程列表
    t_list = []
    # 循环创建线程
    for i in range(5):
        t = Thread(target=func1, args=('t{}'.format(i),))
        t.start()
        t_list.append(t)
    # 等待线程结束
    for t in t_list:
        t.join()

Python 使用线程的时候,会定时释放 GIL 锁,这时会 sleep,所以才会出现上面的问题。 面对这个问题,如果要解决此问题,我们可以使用 Lock 锁解决此问题( 加锁的目的是:保证数据安全)

from threading import Thread,Lock
def func1(name):
    # 获取锁
    lock.acquire()
    with lock:
        global count
        for i in range(100000):
            count += 1
    # 释放锁
    lock.release()
if __name__ == "__main__":
    count = 0
    t_list = []
    # 创建锁对象
    lock = Lock()
    for i in range(10):
        t = Thread(target=func1,args=(f't{i+1}',))
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(count)

七、死锁

from threading import Thread, Lock #Lock 锁 同步锁 互斥锁
from time import sleep
def fun1():
    lock1.acquire()
    print('fun1 拿到键盘')
    sleep(2)
    lock2.acquire()
    print('fun1 拿到鼠标')
    lock2.release()
    print('fun1 释放鼠标')
    lock1.release()
    print('fun1 释放键盘')
def fun2():
    lock2.acquire()
    print('fun2 拿到鼠标')
    lock1.acquire()
    print('fun2 拿到键盘')
    lock1.release()
    print('fun2 释放键盘')
    lock2.release()
    print('fun2 释放鼠标')
if __name__ == '__main__':
    lock1 = Lock()
    lock2 = Lock()
    t1 = Thread(target=fun1)
    t2 = Thread(target=fun2)
    t1.start()
    t2.start()
from threading import RLock
'''
Lock 锁 同步锁 互斥锁
RLock 递归锁
'''
def func1():
    lock.acquire()
    print('func1获取锁')
    func2()
    lock.release()
    print('func1释放锁')
def func2():
    lock.acquire()
    print('func2获取锁')
    lock.release()
    print('func2释放锁')
def func3():
    func1()
    func2()
if __name__ == "__main__":
    #lock = Lock()  会产生错误
    lock = RLock()
    func3()

八、信号量(Semaphore)

我们都知道在加锁的情况下,程序就变成了串行,也就是单线程,而有时,我们在不用考 虑数据安全时,为了避免业务开启过多的线程时。我们就可以通过信号量(Semaphore)来 设置指定个数的线程。(比如:电梯每次只能承载三个人,那么同时只能有三个人乘坐,其他人只能等别人做完才能乘坐)

from time import sleep
from threading import Thread
from threading import BoundedSemaphore
def index(num):
    lock.acquire()
    print(f'第{num}个人乘坐!!')
    sleep(2)
    lock.release()
if __name__ == "__main__":
    lock = BoundedSemaphore(3)
    for i in range(10):
        t = Thread(target=index,args=(f'{i+1}',))
        t.start()

九、事件(Event)

Event()可以创建一个事件管理标志,该标志(event)默认为 False,event 对象主要有 四种方法可以调用:

1、 event.wait(timeout=None):调用该方法的线程会被阻塞,如果设置了 timeout 参数,超时后,线程会停止阻塞继续执行;

2、event.set():将 event 的标志设置为 True,调用 wait 方法的所有线程将被唤 醒;

3、event.clear():将 event 的标志设置为 False,调用 wait 方法的所有线程将被 阻塞;

4、event.is_set():判断 event 的标志是否为 True。

十、线程通信-队列

线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并 行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不 会出现数据污染等意外情况

1使用的队列的好处:

1. 安全

2. 解耦

3. 提高效率

2Queue模块中的常用方法:

Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步

  • Queue.qsize() 返回队列的大小
  • Queue.empty() 如果队列为空,返回True,反之False
  • Queue.full() 如果队列满了,返回True,反之False
  • Queue.full maxsize 大小对应
  • Queue.get([block[, timeout]])获取队列,timeout等待时间
  • Queue.get_nowait() 相当Queue.get(False)
  • Queue.put(item) 写入队列,timeout等待时间
  • Queue.put_nowait(item) 相当Queue.put(item, False)
  • Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
  • Queue.join() 实际上意味着等到队列为空,再执行别的操作

十一、生产者和消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者 彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费 者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列 就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

from threading import Thread
from queue import Queue
from time import sleep
def producer():
    num = 1
    while True:
        print(f'生产了{num}号皮卡丘')
        qe.put(f'{num}号皮卡丘')
        num += 1
        sleep(1)
def consumer():
    print('购买了{}'.format(qe.get()))
    sleep(2)
if __name__ == "__main__":
    # 共享数据的容器
    qe= Queue(maxsize=5)
    # 创建生产者线程
    t1 = Thread(target = producer)
    # 创建消费者线程
    t2 = Thread(target = consumer)
    # 创建消费者线程
    t3 = Thread(target = consumer)
    # 开始工作
    t1.start()
    t2.start()
    t3.start()

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • python基础之并发编程(三)

    目录 一.协程定义和作用 1.使用协程的优点 2.使用协程的缺点 二.Greenlet 的使用 三.Gevent的使用 四.async io 异步 IO 1.asyncio中的task的使用 五.总结 进程与线程的区别: 进程.线程和协程的特点 总结 一.协程定义和作用 协程(coroutine),又称为微线程,纤程.(协程是一种用户态的轻量级线程) 作用:在执行 A 函数的时候,可以随时中断,去执行 B 函数,然后中断继续执行 A 函数 (可以自动切换),单着一过程并不是函数调用(没有调用语句

  • 理论讲解python多进程并发编程

    一.什么是进程 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 二.进程与程序的区别 程序:仅仅是一堆代 进程:是指打开程序运行的过程 三.并发与并行 并发与并行是指cpu运行多个程序的方式 不管是并行与并发,在用户看起来都是'同时'运行的,他们都只是一个任务而已,正在干活的是cpu,而一个cpu只能执行一个任务. 并行就相当于有好多台设备,可以同时供好多人使用. 而并发就相当于只有一台设备,供几个人轮流用,每个人用一会就换另一个人. 所以只有多个cpu才能实现并行,而一个c

  • python并发编程之线程实例解析

    常用用法 t.is_alive() Python中线程会在一个单独的系统级别线程中执行(比如一个POSIX线程或者一个Windows线程) 这些线程将由操作系统来全权管理.线程一旦启动,将独立执行直到目标函数返回.可以通过查询 一个线程对象的状态,看它是否还在执行t.is_alive() t.join() 可以把一个线程加入到当前线程,并等待它终止 Python解释器在所有线程都终止后才继续执行代码剩余的部分 daemon 对于需要长时间运行的线程或者需要一直运行的后台任务,可以用后台线程(也称

  • python基础之并发编程(二)

    目录 一.多进程的实现 方法一 方法二: 二.使用进程的优缺点 1.优点 2.缺点 三.进程的通信 1.Queue 实现进程间通信 2.Pipe 实现进程间通信(一边发送send(obj),一边接收(obj)) 四.Manager管理器 五.进程池 总结 一.多进程的实现 方法一 # 方法包装 多进程 from multiprocessing import Process from time import sleep def func1(arg): print(f'{arg}开始...') sl

  • Python并发编程线程消息通信机制详解

    目录 1 Event事件 2 Condition 3 Queue队列 4 总结一下 前面我已经向大家介绍了,如何使用创建线程,启动线程.相信大家都会有这样一个想法,线程无非就是创建一下,然后再start()下,实在是太简单了. 可是要知道,在真实的项目中,实际场景可要我们举的例子要复杂的多得多,不同线程的执行可能是有顺序的,或者说他们的执行是有条件的,是要受控制的.如果仅仅依靠前面学的那点浅薄的知识,是远远不够的. 那今天,我们就来探讨一下如何控制线程的触发执行. 要实现对多个线程进行控制,其实

  • python基础之并发编程(一)

    目录 一.进程(Process) 二.线程(Thread) 三.并发编程解决方案: 四.多线程实现 (两种) 1.第一种 函数方法 2.第二种 类方法包装 五.守护线程与子线程 1.线程在分法有: 2.守护线程 六.锁 七.死锁 八.信号量(Semaphore) 九.事件(Event) 十.线程通信-队列 1使用的队列的好处: 2Queue模块中的常用方法: 十一.生产者和消费者模式 总结 一.进程(Process) 是一个具有一定独立功能的程序关于某个数据集合的一次运行活动 二.线程(Thre

  • Python中的并发编程实例

    一.简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态.打开文件列表.追踪指令执行情况的程序指针以及一个保存局部变量的调用栈.通常情况下,一个进程依照一个单序列控制流顺序执行,这个控制流被称为该进程的主线程.在任何给定的时刻,一个程序只做一件事情. 一个程序可以通过Python库函数中的os或subprocess模块创建新进程(例如os.fork()或是subprocess.Popen()).然而,这些被称为子进程的进程却是独立运行的,它们有各自独立的系统状态以及

  • Python基础之元编程知识总结

    一.前言 首先说,Python中一切皆对象,老生常谈.还有,Python提供了许多特殊方法.元类等等这样的"元编程"机制.像给对象动态添加属性方法之类的,在Python中根本谈不上是"元编程",但在某些静态语言中却是需要一定技巧的东西.我们来谈些Python程序员也容易被搞糊涂的东西. 我们先来把对象分分层次,通常我们知道一个对象有它的类型,老早以前Python就将类型也实现为对象.这样我们就有了实例对象和类对象.这是两个层次.稍有基础的读者就会知道还有元类这个东西

  • Python并发编程之未来模块Futures

    目录 区分并发和并行 并发编程之Futures 到底什么是Futures? 为什么多线程每次只有一个线程执行? 总结 不论是哪一种语言,并发编程都是一项非常重要的技巧.比如我们上一章用的爬虫,就被广泛用在工业的各个领域.我们每天在各个网站.App上获取的新闻信息,很大一部分都是通过并发编程版本的爬虫获得的. 正确并合理的使用并发编程,无疑会给我们的程序带来极大性能上的提升.今天我们就一起学习Python中的并发编程——Futures. 区分并发和并行 我们在学习并发编程时,常常会听到两个词:并发

  • 详解Python并发编程之创建多线程的几种方法

    大家好,并发编程 今天开始进入第二篇. 今天的内容会比较基础,主要是为了让新手也能无障碍地阅读,所以还是要再巩固下基础.学完了基础,你们也就能很顺畅地跟着我的思路理解以后的文章. 本文目录 学会使用函数创建多线程 学会使用类创建多线程 多线程:必学函数讲解 经过总结,Python创建多线程主要有如下两种方法: 函数 类 接下来,我们就来揭开多线程的神秘面纱. . 学会使用函数创建多线程 在Python3中,Python提供了一个内置模块 threading.Thread,可以很方便地让我们创建多

  • Python基础教程之利用期物处理并发

    前言 抨击线程的往往是系统程序员,他们考虑的使用场景对一般的应用程序员来说,也许一生都不会遇到--应用程序员遇到的使用场景,99% 的情况下只需知道如何派生一堆独立的线程,然后用队列收集结果. 本文章记录了本人在学习Python基础之控制流程篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流. 本文重点: 1.掌握异步编程的相关概念: 2.了解期物future的概念.意义和使用方法: 3.了解Python中的阻塞型I/O函数释放GIL的特点. 一.异步编程相关概念 阻塞:程

  • Python并发编程实例教程之线程的玩法

    目录 一.线程基础以及守护进程 二.线程锁(互斥锁) 三.线程锁(递归锁) 四.死锁 五.队列 六.相关面试题 七.判断数据是否安全 八.进程池 & 线程池 总结 一.线程基础以及守护进程 线程是CPU调度的最小单位 全局解释器锁 全局解释器锁GIL(global interpreter lock) 全局解释器锁的出现主要是为了完成垃圾回收机制的回收机制,对不同线程的引用计数的变化记录的更加精准. 全局解释器锁导致了同一个进程中的多个线程只能有一个线程真正被CPU执行. GIL锁每执行700条指

随机推荐