python 22 线程详解

day 22 线程详解

昨日回顾

1.Queue:进程间通信
	-实例化得到一个对象
	-对象.put()
	-对象.get()
2.生产者消费者模型
3.通过共享变量来共享数据(进程间数据是隔离的)
	-Manager实现多个进程操作同一个变量
	-加锁
4.线程,每个进程下最少有一个线程,cpu调度的最小单位
5.python如何开启线程

今日内容

1.全局解释器锁(GIL)

0 pypy(没有全局解释器锁),比cpython快,但是好多模块用不了 cpython

1.全局解释器锁,GIL锁(cpython解释器的问题)
	-当年python设计的时候,还是单核,没有多核的概念
	-python需要做垃圾回收(gc)
	-垃圾回收线程,进行垃圾回收
	-设计了一个大锁(GIL锁),只有拿到这把锁的线程,才能执行
	-同一时刻,在一个进程中,可以开多个线程,但是只能有一条线程在执行
	-不能利用多核优势

### 只针对于cpython解释器(其他解释器,包括其他语言不这样)
2.如果是计算密集型:要开进程
3.如果是IO密集型:要开线程

2.开启线程的两种方式

from threading import Thread
import time

def task():
    time.sleep(1)
    print("我是子线程")

if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    print("我是主线程")
    

from threading import Thread
import time


class MyThread(Thread):
    def __init__(self, a):
        self.a = a
        super().__init__()

    def run(self):
        time.sleep(1)
        print('我是子线程', self.a)


if __name__ == '__main__':
    t = MyThread('lem')
    t.start()
    print('我是主线程')

3.多线程与多进程比较

3.1 pid比较

3.2 开启速度比较

# 开线程消耗的资源和耗费的时间远远小于开进程
from threading import Thread
import time
from multiprocessing import Process

def task():
    time.sleep(1)
    print("我是子线程")

if __name__ == '__main__':
    t1 = time.time()
    t = Thread(target=task)
    t.start()
    print("我是主线程")
    t.join()
    print(time.time()-t1)
>>>>>>>>
我是主线程
我是子线程
1.0029711723327637


    p = Process(target=task)
    p.start()
    print("我是主进程")
    p.join()
    print(time.time()-t1)
>>>>>>>>
我是主进程
我是子线程
1.5179076194763184

3.3 内存数据的共享问题

from threading import Thread
import time
from multiprocessing import Process


def task():
    global n
    n = 10
    print(n)


if __name__ == '__main__':
    n = 100
    t = Thread(target=task)
    t.start()
    t.join()
    print("主线程")
    print(n)
>>>>>>>>
10
主线程
10

4.Thread类的其他方法

import time
from threading import Thread
import threading

def task():
    time.sleep(0.1)
    print("我是子线程")


if __name__ == '__main__':
    t = Thread(target=task)

    t.start()
    t1 = Thread(target=task)

    t1.start()
    # print(t.is_alive())  # 看线程是否存活
    # print(t.getName())  # 获取线程的名字
    # t.setName('lem')  # 设置线程名字
    # print(t.getName())
    #
    # print("主线程")
    # time.sleep(0.2)
    # print(t.is_alive())

    # 主线程中执行返回当前线程对象
    # res = threading.currentThread()
    # print(res)

    # 返回当前进程中正在运行的子线程对象列表
    # res = threading.enumerate()
    # print(res)

    # 返回当前正在运行的线程个数
    # res = threading.activeCount()
    # print(res)

    # 线程id号
    res = threading.get_ident()
    print("主线程",res)
  


"""
t.is_alive()
t.getName()
t.setName

threading:木块下的一些方法
res = threading.currentThread()
res = threading.enumerate()
res = threading.activeCount()
res = threading.get_ident()
"""

5.join方法

1.等待子线程执行结束

import time
from threading import Thread
import threading


def task(i):
    time.sleep(1)
    print(f"我是子线程{i + 1}")


if __name__ == '__main__':
    t1 = time.time()
    ls = []
    for i in range(100):
        t = Thread(target=task, args=(i,))
        t.start()
        ls.append(t)

    for t in ls:
        t.join()

    print("主线程")
    print(time.time() - t1)

6.守护线程

from threading import Thread
import time


def task():
    time.sleep(2)
    print("我是子线程")


if __name__ == '__main__':
    t = Thread(target=task)
    t.setDaemon(True)  # 守护线程如果主线程执行结束,子线程也结束(不执行了)
    t.start()
    print("主线程结束")

7、同步锁(互斥锁)

## 多个线程操作同一个数据(变量),会出现并发安全的问题
# from threading import Thread,Lock
# import time
# import random
# def task():
#     global n
#
#
#     ### 临界区(加锁)
#     time.sleep(random.random())
#     temp=n
#     time.sleep(random.random())
#     temp=temp-1
#     n=temp
#
#     ##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换
#
#     # n-=1
#
#
# if __name__ == '__main__':
#     n=10
#     ll=[]
#     for i in range(10):
#         t=Thread(target=task)
#         t.start()
#         ll.append(t)
#
#     for i in ll:
#         i.join()
#
#
#     print(n)



###出现了并发安全的问题,加锁解决

from threading import Thread,Lock
import time
import random
def task_lock(lock):
    global n


    ### 临界区(加锁)
    with lock:
        time.sleep(random.random())
        temp=n
        time.sleep(random.random())
        temp=temp-1
        n=temp

    ##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换

    # n-=1

def task_no_lock():

    global n
    time.sleep(random.random())
    temp=n
    time.sleep(random.random())
    temp=temp-1
    n=temp



if __name__ == '__main__':
    n=10
    lock=Lock()
    ll=[]
    for i in range(10):
        # t=Thread(target=task_lock,args=[lock,])
        t=Thread(target=task_no_lock,args=[lock,])
        t.start()
        ll.append(t)
        t.join()

    # for i in ll:
    #     i.join()


    print(n)


'''
互斥锁和join的区别
如果使用互斥锁:只锁临界区,只有临界区是串行,其他地方还是并发的
如果使用join,整个过程全变成串行执行


'''

8.信号量

### 信号量可以理解为多把锁,同时允许多个线程来更改数据

from threading import Thread, Semaphore
import time
import random

def task(sm, i):
    with sm:
        print(f"{i}:正在上厕所")
        time.sleep(random.random())
        print(f"{i}:结束了")


# 信号量可以理解为多把锁,允许多个线程同时进行操作
sm = Semaphore(5)
for i in range(40):
    t = Thread(target=task, args=(sm,i))
    t.start()

9.Event事件

"""
Event事件:
一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号
比如一个线程等待另一个线程执行结束再继续执行
"""

from threading import Thread, Event
import time


def girl(event):
    print("赵丽颖现在在结婚状态")
    time.sleep(1)
    # 离婚了,发送信号
    print("赵丽颖离婚了")
    event.set()  # 发送一个信号


def boy(i, event):
    print(f"屌丝{i}等待赵丽颖的离婚信号")
    event.wait()  # 收不到信号之前,一直卡在这
    print(f"屌丝{i},收到了离婚信号,开始追")


event = Event()
t = Thread(target=girl, args=(event,))

t.start()

for i in range(10):
    t1 = Thread(target=boy, args=(i,event,))
    t1.start()

作业

1.基于线程实现高并发的tcp服务端
2.作业 写两条线程,一条线程读一个文件的头1/2,另一个线程读这个文件的后1/2
但是第一个线程读完发送信号第二个线程才能读。
3.通过线程实现模拟抢票(票数放在变量中即可)
4.验证一些GIL锁的存在()
5.研究一下哲学家就餐问题,死锁。
import time
from threading import Thread, Event, Lock

# 1.基于线程实现高并发的tcp服务端
"1        ==============================================================="
"""
import socket

sk = socket.socket()
sk.bind(("localhost", 9000))
sk.listen()


def reply(conn, addr):
    while True:
        try:
            data = conn.recv(1024)
            if not data: break
            print(addr, data.decode())
            conn.send(data.upper())
        except Exception as e:
            print(e)
            break


while True:
    conn, addr = sk.accept()
    t = Thread(target=reply, args=(conn, addr,))
    t.start()
"""

# 2.作业 写两条线程,一条线程读一个文件的头1/2,另一个线程读这个文件的后1/2,但是第一个线程读完发送信号第二个线程才能读。
"2        ==============================================================="

"""
def read_first(event):
    print("\n读取前一半\n")
    with open('多线程客户端测试.py', 'r', encoding='utf-8') as f:
        data = f.read()
        res = data[:int(len(data) / 2)]
    event.set()
    print(res)


def read_last(event):
    print("\n等待前一半读完\n")
    event.wait()
    print("\n开始读取后一半\n")
    with open('多线程客户端测试.py', 'r', encoding='utf-8') as f:
        data = f.read()
        res = data[int(len(data) / 2):]
    print(res)


event = Event()

t1 = Thread(target=read_first, args=(event,))

t2 = Thread(target=read_last, args=(event,))
t1.start()
t2.start()

"""

# 3.通过线程实现模拟抢票(票数放在变量中即可)
"3        ==============================================================="

"""
tickets = 2


def get_ticket(lock, i):
    global tickets
    with lock:
        if tickets <= 0:
            print("票卖完了")
            return
        time.sleep(0.1)
        print(f"此时还有{tickets}张票")
        tickets = tickets -1
        print(f"{i}:买票成功")
    # lock.release()

lock = Lock()

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

# 4.验证一些GIL锁的存在()
"4        ==============================================================="

"""
def task():
    a = 0
    while True:
        a = a + 1


t1 = Thread(target=task)
t2 = Thread(target=task)
t3 = Thread(target=task)
t4 = Thread(target=task)

t1.start()
t2.start()
t3.start()
t4.start()
"""
# 5.研究一下哲学家就餐问题,死锁。
"5        ==============================================================="

# import json
# import ujson
# import time
#
#
# def cost_time(func):
#     def inner(*args, **kwargs):
#         start_time = time.time()
#         result = func(*args, **kwargs)
#         stop_time = time.time()
#         print(stop_time - start_time)
#         # print result
#         return result
#
#     return inner
#
#
# a = {}
# for i in range(1, 1000000):
#     a[i] = 1
#
#
# @cost_time
# def json_dumps(obj):
#     return json.dumps(obj)
#
#
# @cost_time
# def ujson_dumps(obj):
#     return ujson.dumps(obj)
#
#
# r1 = json_dumps(a)
# r2 = ujson_dumps(a)
posted @ 2021-04-26 15:46  橘丶阳菜  阅读(83)  评论(0编辑  收藏  举报