python-多线程

通过用threading模块的Thread类创建线程

创建线程常用的两个参数:target和args

target表示线程要运行的函数名,不需要加括号'()'

args表示target的函数所需的入参,类型时元组'(xxx,)',函数无入参时该参数可省略

import time
import threading

begin = time.time()
print(begin)
def foo(n):
    print("foo()",n)
    time.sleep(1)#睡一秒
    print("end foo() ->",time.time())

def bar(n):
    print("bar()",n)
    time.sleep(2)#睡两秒
    print("end bar() ->",time.time())

# 创建两个线程
t1 = threading.Thread(target=foo,args=(1,))
t2 = threading.Thread(target=bar,args=(2,))

# 启动线程
t1.start()
t2.start()

end = time.time()
print(end-begin)

线程阻塞:join --> 主线程阻塞,等子线程执行完主线程才可以往下走

线程守护:setDaemon --> 当设为True时,该子线程随主线程结束而结束

当前线程信息:threading.current_thread()

当前线程数:threading.active_count()

import threading
from time import ctime,sleep

def music(func):
# 打印当前线程的信息
print(threading.current_thread()) for i in range(2): print ("Begin listening to %s. %s" %(func,ctime())) sleep(2) print("end listening %s"%ctime()) def move(func):
# 打印当前线程的信息
print(threading.current_thread()) for i in range(2): print ("Begin watching at the %s! %s" %(func,ctime())) sleep(3) print('end watching %s'%ctime()) threads = [] t1 = threading.Thread(target=music,args=('七里香',)) threads.append(t1) t2 = threading.Thread(target=move,args=('阿甘正传',)) threads.append(t2) if __name__ == '__main__':
    # 开启进程守护
t1.setDaemon(True)
for t in threads: # t.setDaemon(True) t.start()

# 进程阻塞
t2.join()
# t1.join()
# 打印当前线程的信息(主线程)
print(threading.current_thread())
# 还在运行线程数
print(threading.active_count()) print ("all over %s" %ctime())

通过继承threading.Thread来创建线程

import time
import threading

#创建一个类继承threading.Thread
class MyThread(threading.Thread):
    #只需要重写run函数即可
    def run(self):
        time.sleep(1)
        print(self.name)

if __name__ == '__main__':
    thrds = []
    for a in range(1,6):
        thrds.append(MyThread())
    for t in thrds:
        t.start()
    for t in thrds:
        t.join()

当多个线程都在同时操作同一个共享资源,可能会造成了资源破坏,可以创建同步锁来解决这个问题

创建同步锁:r = threading.Lock()

开启锁:r.acquire()

释放锁:r.release()

不加这锁的话结果会不一样

加完锁程序变成串行了,每个都会开锁再解锁,一个一个运行

import time
import threading

# 创建锁
r = threading.Lock()

def addNum():
    global num #在每个线程中都获取这个全局变量

    # 开启锁
    r.acquire()

    temp=num
    print('--get num:',num )
    time.sleep(0.0001)
    num =temp-1 #操作

    # 释放锁
    r.release()


num = 100  #设定一个共享变量
thread_list = []
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )

同步锁有时候会造成死锁

import threading,time

#死锁
class myThread(threading.Thread):
    def doA(self):
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        time.sleep(3)
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        lockB.release()
        lockA.release()

    def doB(self):
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        time.sleep(2)
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        lockA.release()
        lockB.release()
    def run(self):
        self.doA()
        self.doB()
if __name__=="__main__":
    #创建两个同步锁
    lockA=threading.Lock()
    lockB=threading.Lock()
    threads=[]
    for i in range(5):
        threads.append(myThread())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

举个为啥用同步锁会造成死锁的例子

得用递归锁解决

#帐户类
class Account:
    def __init__(self,id,money):
        self.id = id
        self.balance = money
        self.r = threading.RLock()

    #支取
    def draw(self,money):
        self.r.acquire()
        self.balance -= money
        self.r.release()

    #存入
    def deposit(self,money):
        self.r.acquire()
        self.balance += money
        self.r.release()

    #既取又存
    def test(self,money):
        #如果不用递归锁这里就可能会出现死锁
        self.r.acquire()
        self.balance -= money
        self.deposit(money)
        self.r.release()

#转帐
def transfer(a,b,money):
    a.draw(money)
    b.deposit(money)

#初始化帐户
a = Account(1001,1000)
b = Account(1002,2000)
#初始化两个转帐线程
t1 = threading.Thread(target=transfer,args=(a,b,100))
t2 = threading.Thread(target=transfer,args=(b,a,200))
#开启两个线程
t1.start()
t2.start()
#开启线程阻塞
t1.join()
t2.join()

递归锁:threading.RLock。可重入锁,就是同一把锁可以锁多次,锁几次就得释放几次,不然其他线程在这个地方都会被锁住。

RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

上面的死锁用递归锁实现

import threading,time

#递归锁(可重用):threading.RLock

class myThread(threading.Thread):
    def doA(self):
        lock.acquire()
        print(self.name,"gotlockA",time.ctime())
        time.sleep(3)
        lock.acquire()
        print(self.name,"gotlockB",time.ctime())
        lock.release()
        lock.release()

    def doB(self):
        lock.acquire()
        print(self.name,"gotlockB",time.ctime())
        time.sleep(2)
        lock.acquire()
        print(self.name,"gotlockA",time.ctime())
        lock.release()
        lock.release()
    def run(self):
        self.doA()
        self.doB()
if __name__=="__main__":
    #创建递归锁
    lock=threading.RLock()
    
    threads=[]
    for i in range(5):
        threads.append(myThread())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

有一类线程需要满足条件之后才能够继续执行,

Python提供了threading.Condition 对象用于条件变量线程的支持,

它除了能提供RLock()或Lock()的方法外

还提供了 wait()、notify()、notifyAll()方法

lock_con=threading.Condition([Lock/Rlock])

锁是可选选项,不传人锁,对象自动创建一个RLock()

wait()方法释放锁并等待notify()方法的激活

 

notify()方法不释放锁,用来激活wait()

 

import time
import threading
import random

#生产者
class Product(threading.Thread):
    def run(self):
        global L
        while True:
            con_lock.acquire()
            val = random.randint(0,100)
            L.append(val)
            print('生产者:',self.name,'Append',str(L))
            con_lock.notify()
            con_lock.release()
            time.sleep(3)

#消费者
class Consume(threading.Thread):
    def run(self):
        global L
        while True:
            con_lock.acquire()
            if len(L) == 0:
                con_lock.wait()
                print('OK')
            val = L.pop(0)
            print('消费者:',self.name,'Delete',val,' Left',str(L))
            con_lock.release()
            time.sleep(0.5)

if __name__ == '__main__':
    L = []
    con_lock = threading.Condition()
    thrs = []
    for a in range(6):
        thrs.append(Product())
    thrs.append(Consume())
    for t in thrs:
        t.start()
    for t in thrs:
        t.join()

信号量:用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。

计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

import time
import threading

#信号量:Semaphore/BoundedSemaphore(类似于停车位的概念)

class MyThread(threading.Thread):
    def run(self):
        semaphore.acquire()
        print(self.name)
        time.sleep(3)
        semaphore.release()

if __name__=="__main__":
    semaphore = threading.BoundedSemaphore(5)
    thrs = []
    for a in range(100):
        thrs.append(MyThread())
    for t in thrs:
        t.start()
    for t in thrs:
        t.join()

多线程利器:queue,即队列

队列的常用操作

#首先 导入queue模块
import queue
#创建一个'队列'对象
q = queue.Queue(maxsize = 10)
# Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。
# 可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。

#将一个值放入队列中
q.put(10)
#调用队列对象的put()方法在队尾插入一个项目。
# put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。
# 如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。

#将一个值从队列中取出
q.get()
#调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。
# 如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。

'''
Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。  class queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。             class queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。   class queue.PriorityQueue(maxsize)
'''

#此包中的常用方法:
q = queue.Queue() #创建一个队列
q.qsize() # 返回队列的大小
q.empty() # 如果队列为空,返回True,反之False
q.full() # 如果队列满了,返回True,反之False
q.full # 与 maxsize 大小对应
q.get(block=False,timeout=3) # block和timeout都不是必填项,block默认True,timeout等待时间默认None
q.get_nowait() # 相当q.get(False)
q.put('item') # 非阻塞时,写入队列,timeout等待时间
q.put_nowait('item') # 相当于 -->  q.put('item', False)

q.task_done() # 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() # 实际上意味着等到队列为空,再执行别的操作

简单的例子

import threading,queue,time,random

class Production(threading.Thread):
    def run(self):
        while True:
            r = random.randint(0,100)
            q.put(r)
            print("生产出来%s号包子"%r)
            time.sleep(1)
class Proces(threading.Thread):
    def run(self):
        while True:
            ret = q.get()
            print("吃掉%s号包子" % ret)
if __name__=="__main__":
    q = queue.Queue(10)
    thrds = [Production(),Production(),Production(),Proces()]
    for t in thrds:
        t.start()

 

 

posted on 2019-11-21 20:07  「枫」  阅读(123)  评论(0编辑  收藏  举报

导航