python学习笔记五:并发


多线程

python提供了thread、threading和Queue模块来支持多线程编程
thread只支持基本的功能,不推荐使用,threading是更加全面,更高级别的模块,
使用Queue可以创建一个队列数据结构,用于在多线程之间进行共享

threading

模块

对象描述
Thread表示一个执行线程的对象
Lock锁原语对象
RLock可重入锁对象,使单一线程可以(在此)获得已持有的锁(递归锁)
Condition条件变量对象
Event条件变量的通用版本,等待的条件满足后唤醒所有等待线程
Semaphone为线程间共享的有限资源提供了一个“计数器”,没有可用资源时阻塞
BoundeSemaphone与Semaphon类似,不过不允许超过初始值
Timer定时器,运行前会等待一段时间
Barrier创建一个“障碍”,必须达到指定数量的线程后才可以继续(PY3.2引入)

属性和方法

属性描述
name线程名
ident线程标识符
daemonbool值,表示这个线程是否是守护线程
__init__(group=None, tatget=None, name=None, args=(),kwargs ={}, verbose=None, daemon=None)实例化一个线程对象,需要有一个可调用的 target,以及其参数 args或 kwargs。还可以传递 name 或 group 参数,不过后者还未实现。此外, verbose 标 志 也 是 可 接 受 的。 而 daemon 的 值 将 会 设定thread.daemon 属性/标志
start()开启线程,自动调用run()方法执行线程代码
run()定义线程功能的方法,用来实现线程的功能与业务逻辑,一般在子类中重写
join(timeout = None)等待线程结束后或者超时返回
is_alive()bool值,表示这个线程是否还存活
active_count()当前活动的Thread对象个数
current_thread()返回当前的 Thread对象
enumerate()返回当前活动的Thread对象列表

设置守护线程daemon属性:
1.daemon=True时主线程结束时不对该子线程进行检查而直接退出,同时所有为True的子线程无论是否运行完成都将随主线程一起结束。
2.daemon=False时主线程结束时会检查该子线程是否运行完成,若该子线程未完成,则主线程会等该子线程运行完成后再退出
多线程时,由于daemon的默认值为False,主线程执行完自己的任务以后会检查子线程是否运行完成,若子线程未完成,则主线程会等该子线程运行完成后再退出。如果需要修改daemon的值,则必须再调用start()方法启动线程之前修改

创建线程

使用Thread类创建线程的方法:

1.创建一个Thead实例, 传给他一个函数
2. 继承Thread类并在子类中重写__init__()和run()方法
3. 创建Thread实例, 传给他一个可调用的类实例 (一般不用)

例一:

#!/usr/bin/python3

from threading import Thread

def func(name):
    print(name)


t = Thread(target = func, args = ("hello world",)) #args的传参须为列表
t.start()
t.join()

互斥锁

Threading.Lock
方法:

  1. acquire()
    获得锁。该方法等待锁被解锁,将其设置为locked并返回True。
  2. release() 释放锁。当锁被锁定时,将其重置为解锁并返回。如果锁未锁定,则会引发RuntimeError。
  3. locked() 如果锁被锁定,返回True

例二+互斥锁:

from threading import Thread, Lock

exitFlag = 0
lock = Lock()

class myThread(Thread):
    def __init__(self, ID, name, counter):
        super().__init__()  		#调用父类__init__
        self.id = ID
        self.name = name		    # 设置name属性
        self.counter = counter
       # self.daemon = True / False 设置是否为守护线程
    
    def run(self):
        print("开始线程:" + self.name)
        lock.acquire()						#加锁
        print_time(self.name, self.counter, 3)
        lock.release()						#释放锁
        print("Done")

def print_time(name, delay, counter):
    while counter:
        if exitFlag:
            print("shutdone")
            name.exit()
        time.sleep(delay)
        print("%s : %s " % (name, time.ctime(time.time())))
        counter -= 1

thread_1 = myThread(3, "lzj", 3)
thread_2 = myThread(6, "wrh", 2)

thread_1.start()
thread_2.start()

thread_1.join()
thread_2.join()

条件变量

此段来自链接

threading.Condition
方法:

  1. acquire() — 线程锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;

  2. release() — 释放锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;

  3. wait(timeout) — 线程挂起(阻塞状态),直到收到一个notify通知或者超时才会被唤醒继续运行(超时参数默认不设置,可选填,类型是浮点数,单位是秒)。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError;

  4. notify(n=1) — 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,缺省参数,默认是通知一个正等待通知的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError,notify()不会主动释放Lock;

  5. notifyAll() — 如果wait状态线程比较多,notifyAll的作用就是通知所有线程

#! /usr/bin/python3
# 导入线程模块
import threading
import time

# 创建条件变量condition
con = threading.Condition()
meat_num = 0

def thread_consumers():
    # 条件变量condition 线程上锁
    con.acquire()
    
    # 全局变量声明关键字 global
    global meat_num
    meat_num = 0

    # 等待肉片下锅煮熟
    con.wait()
    while True:
        print("我来一块肉片...")
        meat_num -= 1
        print("剩余肉片数量:%d"%meat_num)
        time.sleep(0.5)
        if meat_num == 0:
            # 肉片吃光了,通知老板添加肉片
            print("老板,再来一份老肉片...")
            con.notify()
            # 肉片吃光了,等待肉片
            con.wait()

    # 条件变量condition 线程释放锁
    con.release()


def thread_producer():
    # 条件变量condition 线程上锁
    con.acquire()
    # 全局变量声明关键字 global
    global meat_num

    # 肉片熟了,可以开始吃了
    meat_num = 10
    print("肉片熟了,可以开始吃了...")
    con.notify()
    while True:
        # 阻塞函数,等待肉片吃完的通知
        con.wait()
        meat_num = 10
        # 添加肉片完成,可以继续开吃
        print("添加肉片成功!当前肉片数量:%d"%meat_num)
        time.sleep(1)
        con.notify()

    con.release()


if __name__ == "__main__":
    # 创建并初始化线程
    t1 = threading.Thread(target=thread_producer)
    t2 = threading.Thread(target=thread_consumers)

    # 启动线程 -- 注意线程启动顺序,启动顺序很重要
    t2.start()
    t1.start()

    # 阻塞主线程,等待子线程结束
    t1.join()
    t2.join()

    print("程序结束!")

Queue

来自菜鸟
Queue模块是一个提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue

这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。

  • Queue 模块中的常用方法:
  1. Queue.qsize() 返回队列的大小
  2. Queue.empty() 如果队列为空,返回True,反之False
  3. Queue.full() 如果队列满了,返回True,反之False
  4. Queue.full 与 maxsize 大小对应
  5. Queue.get([block[, timeout]]) 获取队列,timeout等待时间
  6. Queue.get_nowait() 相当Queue.get(False)
  7. Queue.put(item) 写入队列,timeout等待时间
  8. Queue.put_nowait(item) 相当Queue.put(item, False)
  9. Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
  10. Queue.join() 实际上意味着等到队列为空,再执行别的操作
#!/usr/bin/python3

import queue		#引入queue模块
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print ("开启线程:" + self.name)
        process_data(self.name, self.q)
        print ("退出线程:" + self.name)

def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()		#互斥锁加锁,取得任务的
        if not workQueue.empty():
            data = q.get()
            queueLock.release()		#解锁
            print ("%s processing %s" % (threadName, data))
        else:
            queueLock.release()
        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()		#创建互斥锁和队列对象
workQueue = queue.Queue(10)
threads = []
threadID = 1

# 创建新线程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 加锁,填充队列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待队列清空
while not workQueue.empty():
    pass

# 通知线程是时候退出
exitFlag = 1

# 等待所有线程完成
for t in threads:
    t.join()
print ("退出主线程")

添加、取出元素要加锁

多进程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值