一、GIL全局解释器锁

  1.GIL

    全局解释器锁  只在Cpython解释器中

    GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全

    用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)

    GIL的存在是因为Cpython解释器的内存管理不是线程安全的

  2.内存管理>>>垃圾回收机制

    (1)引用计数

    (2)标记清楚

    (3)分代回收

  3.同一进程下的多个线程在同一时刻只能有一个线程被执行

  4.线程是直接嫩够被cpu执行吗?

    必须先抢解释器才能被cpu执行

  5.GIL是加在Cpython解释器上的一把锁,并不能保证数据的安全

    如果你想保证数据的安全,就必须加不同的锁

  6.多线程是不是没用?

  例如:四个任务:计算密集的任务  每个任务耗时10s

      单核情况下:

        多线程好一点,消耗的资源少一点

      多核情况下:

        开四个进程:10s多一点

        开四个线程:40s多一点

     四个任务:IO密集的任务  每个任务io 10s

      单核情况下:

        多线程好一点

      多核情况下:

        多线程好一点

    多线程和多进程都有自己的优点,要根据项目需求合理选择

  计算密集型

from multiprocessing import Process
from threading import Thread
import os,time
def work():
    res=0
    for i in range(100000000):
        res*=i


if __name__ == '__main__':
    l=[]
    print(os.cpu_count())  # 本机为8核
    start=time.time()
    for i in range(8):
        # p=Process(target=work) #耗时9.252728939056396
        p=Thread(target=work) #耗时35.369622230529785
        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
def work():
    time.sleep(2)


if __name__ == '__main__':
    l=[]
    print(os.cpu_count()) #本机为8核
    start=time.time()
    for i in range(600):
        p=Process(target=work) #耗时4.699530839920044
        # p=Thread(target=work) #耗时2.054128885269165
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))

  7.补充

    TCP实现并发

  客户端

import socket


client = socket.socket()
client.connect(('127.0.0.1',8080))

while True:
    client.send(b'hello')
    data = client.recv(1024)
    print(data.decode('utf-8'))

  服务端

"""
服务端
    1.要有固定的IP和PORT
    2.24小时不间断提供服务
    3.能够支持并发
"""

server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)


def talk(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0:break
            print(data.decode('utf-8'))
            conn.send(data.upper())
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()

while True:
    conn, addr = server.accept()  # 监听 等待客户端的连接  阻塞态
    print(addr)
    t = Thread(target=talk,args=(conn,))
    t.start()

二、GIL与普通的互斥锁

  对于不同的数据,要想保证安全,需要加不同的锁处理

  GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程

  保证的是同一个进程下多个线程之间的安全

from threading import Thread
import time

n = 100

def task():
    global n
    tmp = n
    # time.sleep(1)
    n = tmp -1

t_list = []
for i in range(100):
    t = Thread(target=task)
    t.start()
    t_list.append(t)

for t in t_list:
    t.join()

print(n)

三、死锁与递归锁

  1.死锁

    每acquire一次锁身上的计数加1

    每release一次锁身上的计数减1

    只要锁的计数不为0,其他人都不能抢

from threading import Thread,Lock,current_thread,RLock
import time

mutexA = Lock()
mutexB = Lock()

class MyThread(Thread):
    def run(self):  # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
        mutexB.acquire()
        print('%s抢到了B锁'%self.name)
        mutexB.release()
        print('%s释放了B锁'%self.name)
        mutexA.release()
        print('%s释放了A锁'%self.name)

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

for i in range(10):
    t = MyThread()
    t.start()

  2.递归锁

    Rlock可以被第一个抢到锁的人连续的acquire和release

from threading import Thread,Lock,current_thread,RLock
import time

mutexA = mutexB = RLock()  # A B现在是同一把锁

class MyThread(Thread):
    def run(self):  # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
        mutexB.acquire()
        print('%s抢到了B锁'%self.name)
        mutexB.release()
        print('%s释放了B锁'%self.name)
        mutexA.release()
        print('%s释放了A锁'%self.name)

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

for i in range(10):
    t = MyThread()
    t.start()

四、信号量

  互斥锁:一个厕所(一个坑位)

  信号量:公共厕所(多个坑位)

from threading import Semaphore,Thread
import time
import random


sm = Semaphore(5)  # 造了一个含有五个的坑位的公共厕所

def task(name):
    sm.acquire()
    print('%s占了一个坑位'%name)
    time.sleep(random.randint(1,3))
    sm.release()

for i in range(40):
    t = Thread(target=task,args=(i,))
    t.start()

五、event事件

from threading import Event,Thread
import time
import random


event = Event()


def light():
    print('红灯亮着!')
    time.sleep(3)
    event.set()  # 解除阻塞,给我的event发了一个信号
    print('绿灯亮了!')


def car(i):
    print('%s 正在等红灯了'%i)
    event.wait()  # 阻塞
    print('%s 加油门飙车了'%i)

t1 = Thread(target=light)
t1.start()


for i in range(10):
    t = Thread(target=car,args=(i,))
    t.start()

六、线程queue

  1.同一进程下的多个线程本来就是数据共享,为什么还要用队列

    因为队列是管道+锁  使用队列你就不需要自己手动操作锁的问题

    因为锁操作的不好极容易产生死锁现象

  2.Queue  普通q

import queue

q=queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())

  3.LifoQueue  先进后出q

import queue

q = queue.LifoQueue(5)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
print(q.get())  # 4

  4.PriorityQueue  优先级q(数值小的先出)

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())
原文地址:https://www.cnblogs.com/yljbky/p/11352568.html