python 多线程编程 各种锁的介绍

大纲:https://docs.python.org/3/library/threading.html? 官方文档

此篇文章结合了官方文档,并参考了一些网络资源,加上自己的一些理解,相当于自己的读书笔记,若有错误之处,请指出。

一.python多线程的基本方法

二.多线程返回值问题

三.线程中锁的问题


一.python多线程的基本方法

python中可利用模块threading进行多线程

class threading.Thread(group=Nonetarget=Nonename=Noneargs=()kwargs={}*daemon=None)

target即为callable object,name为定义的进程名,args为要传递给函数的参数,daemon是否为守护线程,默认为非守护线程,若设置daemon=True,则表明在进程退出的时候,不用等待这个线程退出,即随着其它非守护线程的结束,守护线程结束,守护线程用来守护非守护线程,主线程是非守护线程。

start():开始一个线程

join(timeout=None):阻塞该线程

1. 阻塞主进程,专注于执行多线程中的程序。

2. 多线程多join的情况下,依次执行各线程的join方法,前头一个结束了才能执行后面一个。

3. 无参数,则等待到该线程结束,才开始执行下一个线程的join。

4. 参数timeout为线程的阻塞时间,如 timeout=2 就是罩着这个线程2s 以后,就不管他了,继续执行下面的代码。

name

getName()

setName()

ident

is_alive()

daemon

isDaemon()

setDaemon()

import sys
import time
from threading import Thread,Lock
import threading

ceshi = 1000
def computesum(n,c):
    global ceshi
    sum = 0
    tname = threading.current_thread().name
    ceshi = ceshi - c
    for i in range(n):
        sum = sum + i
    time.sleep(10)
    ceshi = ceshi + c
    print(ceshi)
    return tname



start = time.time()
tdic = {}
lock = Lock()
for i in range(3):
    #共创建了3个线程
    t = Thread(target=computesum,args=(10000000,i))
    #启动线程
    t.start()
    tdic[i] = t

for i in range(2,-1,-1):
    tdic[i].join(timeout=2)
    print(i)
end = time.time()
print(end-start)

二.多线程返回值问题

Thread没有给线程返回值的方法,可以通过利用全局变量来传递的方式或者重写Thread的方法实现

import sys
import time
from threading import Thread,Lock
import threading


#重写Thread
class MyThread(Thread):
    def __init__(self,target,args=()):
        super(MyThread,self).__init__()
        self.target = target
        self.args = args

    def run(self):
        self.result = self.target(*self.args)

    def get_result(self):
        try:
            return self.result
        except Exception:
            return None


ceshi = 1000
#或者此处定义一个全局变量 result
def computesum(n,c):
    global ceshi
    sum = 0
    tname = threading.current_thread().name
    ceshi = ceshi - c
    for i in range(n):
        sum = sum + i
    ceshi = ceshi + c
    print(ceshi)
    #此处将返回的值赋给result
    return tname



start = time.time()
tdic = {}
lock = Lock()
for i in range(3):
    t = MyThread(target=computesum,args=(10000000,i))
    t.start()
    tdic[i] = t

for i in range(2,-1,-1):
    tdic[i].join()
    print(tdic[i].get_result())
end = time.time()
print(end-start)

三.线程中锁的问题

1.Lock

当多个线程修改一个共享数据时,需要进行同步控制操作。

利用lock.acquire()加锁,利用lock.release()释放锁。

每次只要一个线程会获得锁,若一个线程获得锁之后,其它线程试图获取锁,则变成阻塞状态,直到释放锁后,线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁。

acquire(blocking=Truetimeout=-1)

默认blocking=True,即说明一个线程获取锁之后,其它未获得锁的线程会阻塞等待,若blocking=False,则不进行等待,为非阻塞的状态,timeout是设置阻塞等待的最长时间

acquire()的返回值为True or False,表明是否获取到锁?

使用with语句可以进行锁的获取和释放

import sys
import time
from threading import Thread,Lock
import threading


lock = Lock()
ceshi = 0
res = 0
def computesum(n,c):
    global ceshi
    global res
    sum = 0
    tname = threading.current_thread().name
    if lock.acquire():
        print(tname + '获取到')
        ceshi = ceshi - c
        for i in range(n):
            sum = sum + i
        ceshi = ceshi + c
        print(ceshi,tname)
        time.sleep(10)
        lock.release()
    else:
        print(tname+'未获取到')
    return tname



start = time.time()
tdic = {}
for i in range(3):
    t = Thread(target=computesum,args=(10000000,1))
    t.start()
    tdic[i] = t

for i in range(2,-1,-1):
    tdic[i].join()
    print('*'+tdic[i].getName())
end = time.time()
print(end-start)

2.RLock

允许同一线程多次重复上锁,但是注意,上锁的次数必须和释放锁的次数一样,才能真正释放锁,否则会死锁。

使用Lock时,若线程1上锁,若一直不释放锁,线程1再次上锁,会阻塞,但是RLock对相同线程不会出现这种阻塞的情况。

3.Condition

class threading.Condition(lock=None)

不指定lock对象,则会生成一个新的RLock对象

condition可利用wait使当前该线程挂起等待,直到另一个线程的notify方法将waiting的线程唤醒。使用condition可以在资源未达到某线程的条件时就一直处于waiting状态,而不需要重复的去acquire锁进行判断,再release锁。注意,notify之后并不是waiting线程就可以去reacquire锁了,一定要notify之后先release锁。

acquire release wait(等待且释放锁) notify(唤醒并不释放锁)

acquire(*args)

release()

wait(timeout=None)   #到了timeout时间将会reacquire锁

notify(n=1)

notify_all()

import threading
import time
class Producer(threading.Thread):
    # 生产者函数
    def run(self):
        global count
        while True:
            if con.acquire():
                print("生产者获得锁")
                if(count>1000):
                    print("生产者等待")
                    con.wait(timeout=2)
                else:
                    count = count + 100
                    print("生产者",count)
                    if(count>100):
                        con.notify()
                con.release()
            time.sleep(1)

class Consumer(threading.Thread):
    # 消费者函数
    def run(self):
        global count
        while True:
            # 当count 大于等于100的时候进行消费
            if con.acquire():
                print("消费者获得锁")
                if count < 100:
                    print("消费者等待")
                    con.wait()
                else:
                    count = count - 50
                    print("消费者",count)
                    if(count<1000):
                         con.notify()
                con.release()
            time.sleep(1)
                    # 完成生成后唤醒waiting状态的线程,
                    # 从waiting池中挑选一个线程,通知其调用acquire方法尝试取到锁
count =1000
con = threading.Condition()

def test():
    p = Producer()
    p.start()
    c = Consumer()
    c.start()

if __name__ == '__main__':
    test()

4.Semaphore

class threading.Semaphore(value=1)

信号量是一个计数器,当有一个线程acquire,则计数器减1,release则加1,Semaphore不能小于0,计数器等于0时,其它线程会被阻塞(blocking设置为True时)

信号量通常用于保护数量有限的资源,例如数据库服务器。在资源数量固定的任何情况下,都应该使用有界信号量。在生成任何工作线程前,应该在主线程中初始化信号量。

acquire(blocking=Truetimeout=None)

获取一个信号量。

在不带参数的情况下调用时:

  • 如果在进入时,内部计数器的值大于0,将其减1并立即返回true。

  • 如果在进入时,内部计数器的值为0,将会阻塞直到被 release() 方法的调用唤醒。一旦被唤醒(并且计数器值大于0),将计数器值减少1并返回true。线程会被每次 release() 方法的调用唤醒。线程被唤醒的次序是不确定的。

release()

释放一个信号量,将内部计数器的值增加1。当计数器原先的值为0且有其它线程正在等待它再次大于0时,唤醒正在等待的线程。

import time
import threading

s1=threading.Semaphore(10)  

def test():
   while True:
        if(s1.acquire(blocking=False)): 
            print("ok",time.ctime(),threading.current_thread().name)
            time.sleep(10)  
            s1.release()    
        else:
            print(threading.current_thread().name,'未获得')


for i in range(20):
    t1=threading.Thread(target=test,args=())
    t1.start() 

5.Event

class threading.Event

这是线程之间通信的最简单机制之一:一个线程发出事件信号,而其他线程等待该信号。

一个事件对象管理一个内部标志,调用 set() 方法可将其设置为true,调用 clear() 方法可将其设置为false,调用 wait() 方法将进入阻塞直到标志为true。

is_set()

当且仅当内部标志为true时返回true。

set()

将内部标志设置为true。所有正在等待这个事件的线程将被唤醒。当标志为true时,调用 wait() 方法的线程不会被被阻塞。

clear()

将内部标志设置为false。之后调用 wait() 方法的线程将会被阻塞,直到调用 set() 方法将内部标志再次设置为true。

wait(timeout=None)

阻塞线程直到内部变量为true。如果调用时内部标志为true,将立即返回。否则将阻塞线程,直到调用 set() 方法将标志设置为true或者发生可选的超时。

当提供了timeout参数且不是 None 时,它应该是一个浮点数,代表操作的超时时间,以秒为单位(可以为小数)。

当内部标志在调用wait进入阻塞后被设置为true,或者调用wait时已经被设置为true时,方法返回true。 也就是说,除非设定了超时且发生了超时的情况下将会返回false,其他情况该方法都将返回 True 。

import threading
from threading import Event

def test1(e1, e2):
    for item in [1, 3, 5]:
        e1.wait()
        print(item)
        e1.clear()
        e2.set()


def test2(e1, e2):
    for item in [2, 4, 6]:
        e1.wait()
        print(item)
        e1.clear()
        e2.set()

e1, e2 = Event(), Event()
t1 = threading.Thread(target=test1, args=(e1, e2))
t2 = threading.Thread(target=test2, args=(e2, e1))
t1.start()
t2.start()
e1.set()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值