2019.09.18学习整理 锁,多线程vs多进程

2019.09.18学习整理

线程锁

同步锁(线程的互斥锁)

多线程修改数据会造成混乱

from threading import Thread,current_thread,Lock
import time
x = 0

def task():
    global x
    for i in range(100000): # 最少10万级别才能看出来
        x = x+1   # 有可能右边的x刚拿到了0,
        # 发生线程不安全的原因:
        # t1 x+1 阶段 x = 0 保存了状态 cpu被切走  t2 x+1 x = 0 保存了状态 cpu被切走
        # 下次t1 继续运行代码 x = 0+1  下次t2 再运行代码的时候也是 x = 0+1
        #  也就说修改了两次 x 只加了一次1 。
        # time.sleep()
    # lock.release()
if __name__ == '__main__':
    t_list = []
    for i in range(3):
        t = Thread(target=task)
        t_list.append(t)
        t.start()
    for i in t_list:
        i.join()

    print(x) # 99

使用线程锁解决线程修改数据混乱问题

from threading import Thread,current_thread,Lock
import time
x = 0
lock = Lock()
def task():
    global x
    lock.acquire()
    for i in range(100000): # 最少10万级别才能看出来  
        x = x+1   # 有可能右边的x刚拿到了0,
    lock.release()
if __name__ == '__main__':
    t_list = []
    for i in range(3):
        t = Thread(target=task)
        t_list.append(t)
        t.start()
    for i in t_list:
        i.join()

    print(x) # 99

死锁问题

from  threading import Thread,Lock
import time
mutex1=Lock()
mutex2=Lock()

class MyT(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=MyT()
    t.start()
    
    
# 两个线程
# 线程1拿到了(锁头2)想要往下执行需要(锁头1),
# 线程2拿到了(锁头1)想要往下执行需要(锁头2)
# 互相都拿到了彼此想要往下执行的必需条件,互相都不放手里的锁头.

解决方法,递归锁

在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

递归锁

from threading import Thread,Lock,RLock
# 递归锁 在同一个线程内可以被多次acquire
# 如何释放 内部相当于维护了一个计数器 也就是说同一个线程 acquire了几次就要release几次
# mutex1 = Lock()
# mutex2 = Lock()
mutex1 = RLock()
mutex2 = mutex1

import time
class MyThreada(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 = MyThreada()
    t.start()

信号量Semaphore

同进程的一样

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

from threading import Thread,Semaphore
import threading
import time


def task():
    sm.acquire()
    print(f"{threading.current_thread().name} get sm")
    time.sleep(3)
    sm.release()

if __name__ == '__main__':
    sm = Semaphore(5) # 同一时间只有5个进程可以执行。
    for i in range(20):
        t = Thread(target=task)
        t.start()

GIL

在Cpython解释器中有一把GIL锁(全局解释器锁),GIl锁本质是一把互斥锁。
导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势.
同一个进程下多个线程只能实现并发不能实现并行.

为什么要有GIL?

因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁.

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

分析:

我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是:

方案一:开启四个进程
方案二:一个进程下,开启四个线程

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

每个都要计算10s
多线程
在同一时刻只有一个线程会被执行,也就意味着每个10s都不能省,分开每个都要计算10s,共40.ns
多进程
可以并行的执行多个线程,10s+开启进程的时间

io密集型 推荐多线程

4个任务每个任务90%大部分时间都在io.
每个任务io10s 0.5s
多线程
可以实现并发,每个线程io的时间不咋占用cpu, 10s + 4个任务的计算时间
多进程
可以实现并行,10s+1个任务执行的时间+开进程的时间
1751697-20190918154643635-477922303.png

多进程vs多线程

from threading import Thread
from multiprocessing import Process
import time

# 计算密集型
# def work1():
#     res=0
#     for i in range(100000000): #1+8个0
#         res*=i
#
# if __name__ == '__main__':
#     t_list = []
#     start = time.time()
#     for i in range(4):
#         # t = Thread(target=work1)
#         t = Process(target=work1)
#         t_list.append(t)
#         t.start()
#     for t in t_list:
#         t.join()
#     end = time.time()
#     # print('多线程',end-start) # 多线程 15.413789510726929
#     print('多进程',end-start) # 多进程 4.711405515670776


# # io密集型
# def work1():
#     x = 1+1
#     time.sleep(5)
#
#
# if __name__ == '__main__':
#     t_list = []
#     start = time.time()
#     for i in range(4):
#         t = Thread(target=work1)
#         # t = Process(target=work1)
#         t_list.append(t)
#         t.start()
#     for t in t_list:
#         t.join()
#     end = time.time()
#     print('多线程',end-start) #  多线程 5.002625942230225
#     # print('多进程',end-start) # 多进程 5.660863399505615

转载于:https://www.cnblogs.com/zhangmingyong/p/11542783.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值