线程的进一步探究

在cpython(c语言的解释器)中,同一个进程下开启多线程,同一时刻只能有一个线程执行,没有办法利用多和优势
GIL是针对cpython解释器的。
GIL就是一把互斥锁(牺牲了效率但是保证了数据的安全性)
GIL全局解释器存在的原因是应为cpython解释器的内存管理不是线程安全的。
Cpython解释器上有一把GIL全局解释器锁。
同一个进程下的多个线程不能实现并行,但是能够实现并发,多个进程下的线程能够实现并行。
GIL内部有垃圾回收机制

python的垃圾回收机制:1,引用计数 2,标记清楚 3,分代回收

也就是说在同一进程下多个线程是不可能实现并行的但是可以实现并发,相反,多个进程下的线程可以实现并行。

多个线程或进程下的不同情况:

1、基于计算密集型

  在单核情况下:基于计算的使用多线程比较好,因为在相同的进程数下,多线程消耗的资源少

  在多核情况下:开四个进程可能需要10s左右运算结束,而开四个线程需要40S左右

2、基于io密集型

  在单核情况下:多线程相对好一点

  在多个情况下:多线程相比好一点

1、基于计算密集型:

from multiprocessing import Process
from threading import Thread
import os,time
def work():
    res = 0
    for i in range(1000000):
        res*=i
if __name__ == '__main__':
    l=[]
    print(os.cpu_count()) #获取机器核心数
    start = time.time()
    for i in range(8):
        p = Process(target=work)    #进程    # 0.5842239856719971
        # p = Thread(target=work)     #线程   # 0.00799560546875
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop = time.time()
    print('run time is %s' %(stop-start))

2、基于io型

from multiprocessing import Process
from threading import Thread
import threading
import os,time
def work():
    time.sleep(2)

if __name__ == '__main__':
    l=[]
    print(os.cpu_count()) # 本机核心数
    start=time.time()   # 当前时间
    for i in range(400):    # 400个并发
        # p = Process(target=work)    #进程 28.971384525299072
        p = Thread(target=work)     #线程 2.0688133239746094
        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
    mutex.acquire() #加锁
    tmp = n
    time.sleep(0.1)
    n = tmp -1
    mutex.release() #解锁
t_list =[]
for i in range(100): #开启100个线程
    t = Thread(target=task)
    t.start()
    t_list.append(t)
for t in t_list:
    t.join()
print(n)    #输出 0 
“”“
对于不同的数据,想要保证安全,需要加不同的锁处理
GIL并不能保证数据的安全,它是多Cpython解释器加锁,针对的是线程
保证的是同一个进程下多个线程的安全
”“”

死锁和递归锁

递归锁:

# 死锁和递归
from threading import Thread,Lock,RLock
import time

'''
自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续acquire,每次acquire一次计数加一:针对的是第一次抢到我的人
'''


mutexA = mutexB = RLock()  # 抢锁后会有一个计数,抢一次计数加一,针对的是第一个抢到我的人
class Mythead(Thread):
    def run(self):
        self.func1()
        self.func2()
    def func1(self):
        mutexA.acquire()
        print('%s 抢到A-1锁了' % self.name)
        mutexB.acquire()
        print('%s 抢到B-2锁了' % self.name)
        mutexB.release()
        print('%s 释放了B-1-1锁' % self.name)
        mutexA.release()
        print('%s 释放了A-2-1锁' % self.name)


    def func2(self):
        mutexB.acquire()
        print('%s 抢到B-3-1锁了' % self.name)
        time.sleep(1)
        mutexA.acquire()
        print('%s 抢到A-4-1锁了' % self.name)
        mutexA.release()
        print('%s 释放了A-4-2锁' % self.name)
        mutexB.release()
        print('%s 释放了B-3-2锁' % self.name)

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

死锁:

from threading import Thread,Lock,RLock
import time

'''
自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续acquire,每次acquire一次计数加一:针对的是第一次抢到我的人
'''
mutexA = Lock()
mutexB = Lock()

# mutexA = mutexB = RLock()  # 抢锁后会有一个计数,抢一次计数加一,针对的是第一个抢到我的人
class Mythead(Thread):
    def run(self):
        self.func1()
        self.func2()
    def func1(self):
        mutexA.acquire()
        print('%s 抢到A-1锁了' % self.name)
        mutexB.acquire()
        print('%s 抢到B-2锁了' % self.name)
        mutexB.release()
        print('%s 释放了B-1-1锁' % self.name)
        mutexA.release()
        print('%s 释放了A-2-1锁' % self.name)


    def func2(self):
        mutexB.acquire()
        print('%s 抢到B-3-1锁了' % self.name)
        time.sleep(1)
        mutexA.acquire()
        print('%s 抢到A-4-1锁了' % self.name)
        mutexA.release()
        print('%s 释放了A-4-2锁' % self.name)
        mutexB.release()
        print('%s 释放了B-3-2锁' % self.name)

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

线程Queue

普通queue先进先出
import 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
'''
# 先进后出
import queue
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
'''
# 优先级
import queue
q = queue.PriorityQueue()
q.put((10,'a'))
q.put((-1,'b'))
q.put((100,'c'))
print(q.get())
print(q.get())
print(q.get())
'''
(-1, 'b')
(10, 'a')
(100, 'c')
'''

信号量

自定义的互斥锁如果是一个厕所,那么信号量就相当于公共厕所,门口挂着多个厕所的钥匙。抢和释放跟互斥锁一致

from threading import Thread,Semaphore
import time
import random
sm = Semaphore(5)
def task(name):
    sm.acquire()
    print('%s正在蹲坑'%name)
    # 模拟蹲坑耗时
    time.sleep(random.randint(1,5))
    sm.release()
if __name__ == '__main__':
    for i in range(20):
        t = Thread(target=task,args=('伞兵%s号'%i,))
        t.start()
‘’‘
同时进行五个线程操作,超过五个进入等待状态,当有新的线程结束运行等待的线程就会加入
‘’‘

Event事件

一些线程需要等待另外一些线程运行完毕才能运行,类似汽车过红绿灯

# event事件
from threading import Event,Thread
import time
event = Event()
def light():
    print('红灯')
    time.sleep(3)
    event.set()
    print('绿灯')
def car(i):
    print('%s等红灯'%i)
    event.wait()
    print('%s 绿灯行'%i)
t1 = Thread(target=light)
t1.start()

for i in range(5):
    t = Thread(target=car,args=(i,))
    t.start()
'''
红灯
0等红灯
1等红灯
2等红灯
3等红灯
4等红灯
绿灯
1 绿灯行
2 绿灯行
4 绿灯行
3 绿灯行
0 绿灯行
'''
原文地址:https://www.cnblogs.com/yangzhaon/p/10835497.html