首页 > 技术文章 > day20.线程2

lzl121 2021-04-25 12:16 原文

1 全局解释器锁(GIL)

详见:https://www.cnblogs.com/lzl121/p/14699788.html

     cpython
    -pypy python好多模块用不了,
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('我是主线程')
#
#


###第二种方式

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('aaaaa')
    t.start()
    print('我是主线程')

3 多线程与多进程比较

3.1 开启速度比较

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

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


if __name__ == '__main__':
    ####线程
    # ctime=time.time()
    # t=Thread(target=task)
    # t.start()
    # t.join() # 等待子线程执行完成主线程再执行
    # print('我是主线程')
    # print(time.time()-ctime)

    ##进程
    ctime=time.time()
    t=Process(target=task)
    t.start()
    t.join() # 等待子线程执行完成主线程再执行
    print('我是主线程')
    print(time.time()-ctime)

3.2 内存数据的共享问题

##线程间数据共享
from threading import Thread
import time
import os
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)


4 Thread类的其他方法


from threading import Thread
import threading
import time
def task():
    # time.sleep(0.01)
    #在子线程中执行
    # res = threading.currentThread()
    # print(res)

    res=threading.get_ident()
    print('子线程:',res)
    print('我是子线程')


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

    t.start()
    t1.start()
    # print(t.is_alive())  #看线程是否存活
    #
    # print(t.getName() )  # 获取线程的名字
    # t.setName('lqz')   # 设置线程名字
    # print(t.getName() )
    #
    #
    # print('主线程')
    # time.sleep(0.02)
    # 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('lqz') 
   
   threading:模块下的一些方法
   res=threading.currentThread()
   res=threading.enumerate()
   res=threading.activeCount()
   res=threading.get_ident()
   '''

5 join方法

1 等待子线程执行结束


from threading import Thread
import time

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


if __name__ == '__main__':
    ll=[]
    for i in range(1000):
        t=Thread(target=task)

        t.start()
        ll.append(t)

    for i in ll:
        i.join()



    # 主线程等待子线程执行完再执行
    print('我是主线程,子线程全都执行完了')


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):
    sm.acquire()
    print('%s:这个人在上厕所'%i)
    time.sleep(random.random())
    print('%s:这个人拉完了'%i)
    sm.release()



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
import random


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


def boy(i, event):
    print('屌丝%s:在等赵丽颖的离婚信号'%i)
    event.wait()  # 收不到信号之前,一直卡在这
    print('屌丝%s号,收到了离婚信号,开始追' % 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()



##  写两条线程,一条线程读一个文件的头2分之一,另一个线程读这个文件的后2分之一,但是必须第一个线程读完,发送信号后,第二个线程才能读

推荐阅读