并发编程之互斥锁的几种应用

一,GIL全局解释器锁

1.1GIL也是互斥锁的一种,相当于执行权限,每个进程都会存在一把GIL锁,同一进程下的多个线程必须抢到GIL之后才能使cpython解释器来执行自己的代码,即同一进程下的多线程无法实现并行,但是能实现并发,它主要调节了多线程对解释器的竞争变得有序

1.2为何要有GIL

因为Cpython解释器的垃圾回收机制不是线程安全的

补充知识点:

计算密集型:应该使用多进程

ltiprocessing import Process
from threading import Thread
import os,time

#纯计算型
def work():
    res = 0
    for i in range(100000):
        res *=i


if __name__ == '__main__':
    l = []
    print(os.cpu_count())  #电脑中cpu的个数
    start = time.time()
    for i in range(6):
        p = Process(target=work)    #run time is 0.17092013359069824
        # p = Thread(target=work)   #run time is 0.024586200714111328
        l.append(p)
        p.start()

    for p in l:
        p.join()

    stop = time.time()
    print('run time is %s' %(stop-start))

IO密集型:应该开启多线程

from multiprocessing import Process
from threading import Thread
import threading
import os,time

#睡眠是纯IO型
def work():
    time.sleep(2)


if __name__ == '__main__':
    l = []
    start = time.time()
    for i in range(20):
        p = Process(target=work)     #run time is 2.40376615524292
        # p = Thread(target=work)    #run time is 2.0061230659484863
        l.append(p)
        p.start()
    for p in l:
        p.join()

    stop = time.time()
    print('run time is %s' %(stop-start))

二,自定义锁与GIL锁

有个GIL锁只能控制多个线程并发执行的顺序,并不能解决全局变量中的多线程竞争的很乱问题,这是就必须使用自定义锁

from threading import Thread,Lock
import time

mutex = Lock()
n = 100
def task():
    global n
    with mutex: #自定义锁
        temp = n
        time.sleep(0.1)
        n = temp-1


if __name__ == '__main__':
    l=[]
    for i in range(100):
        t = Thread(target=task)
        l.append(t)
        t.start()
    for t in l:
        t.join()
    print(n)

最后的输出结果是0

二,死锁与递归锁

死锁现象:一个进程里有两把锁出现多个线程开始竞争这两把锁,每个线程里面定义两个函数,一个先抢锁A,然后再抢锁B,另一个函数先抢锁B然后再抢锁A,这样一个线程正好抢到锁A想要去抢锁B,另一个线程正好抢到锁B,想要去抢锁A.这两个线程互相拥有各自想要得到的锁,都阻塞到了抢另一把锁的代码中,使整个程序不能继续往下运行,就出现了死锁现象.(解决方法:递归锁)

递归锁:说的简单一点就是把几把锁合并成一把锁,每个线程都在抢这一把锁,只要抢到了,里面的数字就由原来的0改成1,这个线程只要抢到这把锁,就可以给它上好几次锁,也就是把里面的数字增加,其他线程只要看到锁里面的数字不是0就会等着,直到抢到这把锁的线程把数字都释放为0时,其他线程才能继续抢锁.(特点:可以连续acquire)

from threading import Thread,Lock,RLock
import time
#mutexA = Lock()
#mutexB = Lock()   这样是不能解决问题,反而出现死锁现象
mutexA=mutexB=RLock()   #递归锁可以无限递归下去

class Mythead(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s抢到A锁' %self.name)
        mutexB.acquire()
        print('%s抢到B锁' %self.name)
        mutexB.release()
        mutexA.release()

    def f2(self):
        mutexB.acquire()
        print('%s抢到了B锁' %self.name)
        time.sleep(2)
        mutexA.acquire()
        print('%s抢到了A锁' %self.name)
        mutexA.release()
        mutexB.release()

if __name__ == '__main__':
    for i in range(10):
        t = Mythead()
        t.start()

三,信号量

导入一个Semaphore方法,限制并行的线程个数(区别于互斥锁,将多个编程编程串行),将多个并行的线程,控制为几个并行,如果超过了数量,没有抢到的就会等待,直到有的线程执行完毕.

from threading import Thread,Semaphore
import time,random
sm = Semaphore(5)  #控制并行的线程个数为5

def task(name):
    sm.acquire()
    print('%s正在执行' %name)
    time.sleep(random.randint(1,3))
    sm.release()
    
if __name__ == '__main__':
    for i in range(20):  #同时产生了20个线程
        t = Thread(target=task)
        t.start()

四,Event事件

一个进程中存在两种不同的线程,也就是定义两个函数(或者类),一种线程满足什么条件时在Event()容器里放一个可以通行的信号True( event.set()),另一类线程在没有接受到可以通行的信号的时候都等待在(event.wait())这里,当接受到可以通行的信号的时候会并行通过


event.isSet():返回event的状态值;

event.wait():如果 event.isSet()==False将阻塞线程

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统                              调度

event.clear():恢复event的状态值为False


from threading import Thread,Event
import time

event = Event()  #相当于存放通过信号的容器

def light():
    print('红灯亮着')
    time.sleep(3)
    event.set()  #绿灯亮

def car(name):
    print('车%s正在等绿灯' %name)
    event.wait()  #等绿灯
    print('车%s通行' %name)

if __name__ == '__main__':
    #红绿灯
    t1= Thread(target=light)
    t1.start()
    #车
    for i in range(3):
        t = Thread(target=car,args = (i,))
        t.start()

执行结果如下:

红灯亮着
车0正在等绿灯
车1正在等绿灯
车2正在等绿灯
车2通行
车0通行
车1通行

五,线程queue

存在三种形式,(1)先进先出   

import queue

queue.Queue() #先进先出
q=queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())

执行的结果如下:

1
2
3

(2)先进后出

import queue
queue.LifoQueue() #后进先出->堆栈
q=queue.LifoQueue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())

执行结果如下

3
2
1

(3)按优先级取值

import queue
queue.PriorityQueue() #按优先级(优先级用数字表示,数字越小表示的优先级越高)
q = queue.PriorityQueue(3)
q.put((10,'a'))
q.put((-1,'b'))
q.put((100,'c'))
print(q.get())
print(q.get())
print(q.get())

执行结果如下:

b
a
c

猜你喜欢

转载自blog.csdn.net/qq_42737056/article/details/82497136
今日推荐