线程锁

线程锁

from threading import Thread

x = 0

def task():
    global x
    for i in range(200000):
        x = x + 1

if __name__ == '__main__':
    t1 = Thread(target=task)
    t2 = Thread(target=task)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(x)

# 337204

当线程t1,刚拿到x,还未进行加值和赋值操作的时候,就被CPU切走了,这时t1的x保存的是刚拿到数据的状态;然后线程t2开始加值操作,当t2被CPU切走时,x已经加了很多次了,但是t1的x还是原来的数据,然后线程t1重新开始加值操作,还是用原先x的数据,造成了数据安全的问题,

所给线程加一把锁,以此保证数据的安全

from threading import Thread,Lock

x = 0
mutex = Lock()
def task:
    global x
    mutex.acquire()
    for i in range(200000):
        x = x + 1
    mutex.release()

if __name__ == '__main__':
    t1 = Thread(target=task)
    t2 = Thread(target=task)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(x)

# 400000

死锁问题

import time
from threading import Thread,Lock

mutex1 = Lock()
mutex2 = Lock()

class MyThread(Thread):
    def run(self):
        self.task1()
        self.task2()
        
    def task1(self):
        mutex1.acquire()
        print(f'{self.name} 抢到了锁1')
        mutex2.acquire()
        print(f'{self.name} 抢到了锁2')
        mutex2.release()
        print(f'{self.name} 释放了锁2')
        mutex1.release()
        print(f'{self.name} 释放了锁1')
        
    def task2(self):
        mutex2.acquire()
        print(f'{self.name} 抢到了锁2')
        time.sleep(1)
        mutex1.acquire()
        print(f'{self.name} 抢到了锁1')
        mutex1.release()
        print(f'{self.name} 释放了锁1')
        mutex2.release()
        print(f'{self.name} 释放了锁2')
        
for i in range(3):
    t = MyThread()
    t.start()

线程1拿到了锁头2,想要往下执行需要锁头1,

线程2拿到了锁头1,想要往下执行需要锁头2,

线程1和线程2互相拿到了彼此想要往下执行的必须条件,但是互相都不放弃手里的锁头。

递归锁

递归锁:在同一个线程内可以被多次acquire

如何释放:内部相当维护了一个计数器,也就是说同一个线程acquire了几次就要release几次

import time
from threading import Thread,RLock

mutex1 = RLock()
mutex2 = mutex1

class MyThread(Thread):
    def run(self):
        self.task1()
        self.task2()
        
    def task1(self):
        mutex1.acquire()
        print(f'{self.name} 抢到了锁1')
        mutex2.acquire()
        print(f'{self.name} 抢到了锁2')
        mutex2.release()
        print(f'{self.name} 释放了锁2')
        mutex1.release()
        print(f'{self.name} 释放了锁1')
        
    def task2(self):
        mutex2.acquire()
        print(f'{self.name} 抢到了锁2')
        time.sleep(1)
        mutex1.acquire()
        print(f'{self.name} 抢到了锁1')
        mutex1.release()
        print(f'{self.name} 释放了锁1')
        mutex2.release()
        print(f'{self.name} 释放了锁2')
        
for i in range(3):
    t = MyThread()
    t.start()

信号量

Semaphore管理一个内置的计数器,每当调用acquire()时内置计数器-1,调用release()时内置计数器+1;当计数器为0时,acquire()会阻塞线程,直到其他线程调用release()

实例:同时只有5个线程可以获得semaphore,即可以限制最大的连接数为5

import time
from threading import Thread,Semaphore,currentThread

def task():
    sm.acquire()
    print(f'{currentThread().name} 在执行')
    time.sleep(3)
    sm.release()
    
sm = semaphore(5)
for i in range(15):
    t = Thread(target=task)
    t.start()

信号量和进程池是完全不同的概念,进程池Pool(4),最多产生4个进程,而且只有这四个进程,不会产生其他的进程,信号量是产生一堆的线程/进程。

GIL

GIL锁:全局解释器锁

在CPython解释器中有一把GIL锁,GIL锁本质上是一把互斥锁。

为什么要有GIL锁?

因为CPython自带的垃圾回收机制不是线程完全的,所以要有GIL锁。

影响

这就导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势。

同一个进程下多个线程只能实现并发不能实现并行。

多进程vs多线程

计算密集型,推荐使用多进程

IO密集型,推荐使用多线程

上一篇:python多线程需要同步么?


下一篇:Eureca Ribbon分布负载均衡的helloworld例子