疯狂python笔记--并发编程

单线程编程是一条顺序执行流,从上到下依次执行代码,如果遇到阻塞程序将会停滞在这里。

        当然,单线程十分有限,多线程相当于使用多个执行流且互相并不会发生干扰,多线程编程包含有创建、启动线程、控制线程三个方面。

线程与进程

        进程包含有三个特征:独立性(独立的实体,拥有自己独立的资源,并有私有地址空间,未经过允许其他进程无法直接访问地址空间)、动态性(是静态指令,通过加入时间概念,使得进程具有生命周期)、并发性(多个进程可以在单一处理器并发执行,互不干扰)。

线程:轻量级的进程,是进程的执行单元,独立、并发的执行流互相影响:一个线程可以创建和撤销另外一个线程。共享内存、文件句柄和其他进程应有的状态。

线程共享环境有:进程代码段、进程公有数据,方便实现通信

线程创建和启动

使用_thread和threading支持多线程。

#线程
import threading
def action(max):
    for i in range(max):
        print(threading.current_thread().getName() + " " + str(i) )
for i in range(100):
    print(threading.current_thread().getName() + " " + str(i))
    if i == 20:
        t1 = threading.Thread(target = action, args = (100,))
        t1.start()
        t2 = threading.Thread(target = action, args = (100,))
        t2.start()
print("main progess mission complete")

创建循环执行线程。多线程意义:如果不使用多线程,主程序直接调用两次action()函数,程序必须等第一次调用action()执行完成,才会执行第二次调用。

当然可以创建类继承线程thread

mport threading

class FkThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.i = 0
    def run(self):
        while self.i < 20:
            print(threading.current_thread().getName() + " " + str(i) )
            self.i += 1
for i in range(20):
    print(threading.current_thread().getName() + " " + str(i) )
    if i == 10:
        ft1 = FkThread()
        ft1.start()
        ft2 = FkThread()
        ft2.start()
print("main process mission complete")

线程生命周期

CPU需要在多个线程之间切换,所以线程也将多次在运行、就绪之间转换。

新建、就绪、运行、阻塞、死亡五个状态。

新建

        程序主要为主程序,线程进入就绪状态,等待执行。

import threading 
def action(max):
    for i in range(max):
        print(threading.current_thread().getName() + " " + str(i) )
for i in range(20):
    print(threading.current_thread().getName() + " " + str(i) )
    if i == 5:
        threading.Thread(target = action, args= (20, )).run()
        threading.Thread(target = action, args= (20, )).run()

运行和阻塞

        就绪状态线程得到CPU,执行run()方法的线程,线程处于运行状态。

  1.  调用sleep()方法主动放弃所占用处理器资源,线程调用一个阻塞式I/O方法,返回之前线程被阻塞。
  2.  试图获得一个锁对象,但是锁对象被其他线程持有。
  3.  等待某个通知。

线程死亡

        抛出错误或者正常结束,用is_alive()方法判断是否死亡。

控制线程

join线程

将一个大问题分解成小问题,在同一个组里面分配不同线程执行。

import threading
def action(max):
    for i in range(max):
        print(threading.current_thread().getName() + " " + str(i) )
#启动子线程
threading.Thread(target = action, args = (100,), name = "新线程").start()
for i in range(100):
    if i == 20:
        jt = threading.Thread(target = action, args = (100,), name = "被join的线程")
        jt.start()
        #线程等jt执行结束才向下执行
        jt.join()
    print(threading.current_thread().name + " " + str(i))

后台线程

当所有的前台线程死亡后,后台线程也会自动死亡。

import threading
def action(max):
    for i in range(max):
        print(threading.current_thread().name + " " + str(i))
t = threading.Thread(target = action, args = (100,), name= "后台线程")
#可以将线程设置为后台线程
t.daemon = True
t.start()
for i in range(10):
    print(threading.current_thread().name + " " + str(i))
    

线程同步

需要考虑线程安全问题,需要设置锁。

同步锁(Lock)

class X:
    def M():
        self.lock.acquire()
        try:
            pass
        finally:
            self.lock.release()

一个做一个简单的账户取钱程序。

import threading
import time

class Account:
    def __init__(self, account_no, balance):
        self.account_no = account_no
        self._balance = balance
        self.lock = threading.RLock()
    def getBalance(self):
        return self._balance
    def draw(self, draw_amount):
        self.lock.acquire()
        try:
            if self._balance >= draw_amount:
                print(threading.current_thread().name\
                     + "取钱成果,钞票:" + str(draw_amount))
                time.sleep(0.01)
                self._balance -= draw_amount
                print("\t 余额为:" + str(self._balance))
            else:
                print(threading.current_thread().name \
                     + "取钱失败,余额不足")
        finally:
            self.lock.release()
def draw(account, draw_amount):
    account.draw(draw_amount)
acct = Account("1234567", 100)
threading.Thread(name= "甲", target = draw , args =(acct, 800)).start()
threading.Thread(name= "乙", target = draw , args =(acct, 800)).start()

采用策略有:1.不要对线安全类所有方法都进行同步,只对那些改变竞争资源的方法同步。2.可变类有两种运行环境,单线程环境和多线程环境。

死锁

这个需要注意,时序和定时锁场景。

线程通信

使用队列控制线程通信

import threading
import time
import queue

def product(bq):
    str_tuple = ("python", "Kotlin", "Swift")
    for i in range(9999):
        print(threading.current_thread().name + "生产者准备生产")
        time.sleep(0.2)
        #将生产完产品放入队列,如果队列满了阻塞
        bq.put(str_tuple[i % 3])
        print(threading.current_thread().name + "生产者生产完成")
def consume(bq):
    while True:
        print(threading.current_thread().name + "消费者准备消费")
        time.sleep(0.2)
        #取出队列中的产品,如果队列为空,阻塞
        t = bq.get()
        print(threading.current_thread().name + "消费者消费[%s]完成" % t)
bq = queue.Queue(maxsize = 1)
threading.Thread(target = product, args = (bq,)).start()
threading.Thread(target = product, args = (bq,)).start()
threading.Thread(target = product, args = (bq,)).start()

threading.Thread(target = consume, args = (bq,)).start()

用Event可以实现事件以控制线程通信效果

import threading
import time
event = threading.Event()
def cal(name):
    print("%s 启动" % threading.currentThread().getName())
    print("%s 准备开始计算" % name)
    event.wait()
    print("%s 收到通知" % threading.currentThread().getName())
    print("%s 正式开始计算" % name)
threading.Thread(target = cal, args = ("甲",)).start()
threading.Thread(target = cal, args = ("乙",)).start()
time.sleep(2)
print("--------------------")
print("main thread event")
event.set()

线程池

线程池基类是concurrent.futures的Executor。使用线程池或者进程池并发编程,将相应的task函数提交给线程池或进程池就好。

程序将task函数提交到线程池,submit返回一个future对象,获取线程任务函数的返回值。

from concurrent.futures import ThreadPoolExecutor
import threading
import time

def action(max):
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + " " + str(i))
        my_sum += i
    return my_sum
with ThreadPoolExecutor(max_workers = 2) as pool:
    future1 = pool.submit(action, 10)
    future2 = pool.submit(action, 20)
    def get_result(future):
        print(future.result())
    print(future1.done())
    time.sleep(3)
    print(future2.done())
    future1.add_done_callback(get_result)

用with上下文管理器进行分析,shoutdown节省关闭代码。

多进程

程序启动两个进程,一个是父进程,一个是子进程。

import multiprocessing
import os
def action(max):
    for i in range(max):
        print("(%s)子进程, 父进程(%s) %d" %(os.getpid(), os.getppid(),i))
if __name__ == "__main__":
    for i in range(100):
        print("(%s)子进程, %d" %(os.getpid(), i))
        if i == 20:
            mp1 = multiprocessing.Process(target = action, args = (100, ))
            mp1.start()
            mp2 = multiprocessing.Process(target = action, args = (100, ))
            mp2.start()
            mp2.join()
    print("main process mission complete")

当然,进程同样可以向线程一样进行继承。

import multiprocessing
import os

class MyProcess(multiprocessing.Process):
    def __init__(self, max):
        self.max = max
        super().__init__()
    def run(self):
        for i in range(self.max):
            print("(%s)子进程, 父进程(%s) %d" %(os.getpid(), os.getppid(),i))


if __name__ == "__main__":
    for i in range(100):
        print("(%s)子进程, %d" %(os.getpid(), i))
        if i == 20:
            mp1 = MyProcess(100)
            mp1.start()
            mp2 = MyProcess(100)
            mp2.start()
            mp2.run()
    print("main process mission complete")

Context和启动进程的方式

python支持三种方法启动进程,spawn:启动全新的python解释器。fork:子进程继承父进程所有资源,子进程基本等于父进程。forkserver:启动一个服务器进程。显示启动进程。

import multiprocessing
import os
def foo(q):
    print("被启动新进程(%s) %d" %(os.getpid(), i))
    q.put("python")
if __name__ == '__main__':
    ctx = multiprocessing.get_context('fork')
    q = ctx.Queue()
    mp = ctx.Process(target = foo, args = (q, ))
    mp.start()
    print(q.get())
    mp.join()

windows使用

multiprocessing.get_context('fork')

函数获取context进行内容设置。

使用进程池管理进程

import multiprocessing
import time
import os

def action(name = "default"):
    print("被启动新进程(%s) %d" %(os.getpid(), i))
    time.sleep(3)
    
if __name__ == "__main__":
    pool = multiprocessing.Pool(processes = 4)
    pool.apply_async(action)
    pool.apply_async(action, args =("位置参数", ))
    pool.apply_async(action, kwds = {"name": "关键字参数"})
    pool.close()
    pool.join()

进程通信

        两种机制进行通信:Queue从一个进程向另外进程中读取信息;pipe:产生两个连接端,交给通信的两个进程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python并发编程是指使用Python编写并行执行的程序。它允许多个任务同时执行,提高程序的效率。在Python中,有多种方法可以实现并发编程,其中包括多线程、多进程和异步编程。多线程是指在一个程序中同时运行多个线程,每个线程执行一个任务。多进程是指在一个程序中同时运行多个进程,每个进程执行一个任务。异步编程是指通过回调函数或协程来实现并发执行任务的方式。这些方法可以根据需求选择合适的方式来进行并发编程并提高程序的性能。其中,学习Python并发编程推荐在Linux上学习,因为Linux系统对于进程之间的通信有较好的支持,而Mac系统的核心也是Linux,所以Linux上的任何Python代码在Mac上都是可行的。官方文档提供了有关Python并发编程的详细信息,可以在https://docs.python.org/3/library/multiprocessing.html进行查阅。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Python笔记.docx](https://download.csdn.net/download/bxy0806/88235414)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [深入浅出讲解Python并发编程](https://blog.csdn.net/wanger5354/article/details/122016057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [python并发编程](https://blog.csdn.net/weixin_43915382/article/details/122003007)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值