线程

一.线程

1.什么是线程(thread)

  线程:线程是cpu处理的单位(实体),线程存在于进程中.线程没有主次之分,是平等的.

2.线程的两种创建方式

from threading import Thread
import time

def f1(n):
    time.sleep(1)
    print(f"{n}号线程")

def f2(n):
    print(f"{n}号线程")

if __name__ == '__main__':
    t1 = Thread(target=f1,args=(1,))    # 创建线程   和创建进程一样
    t2 = Thread(target=f2,args=(2,))

    t1.start()
    t2.start()
    print(t1.ident)
    # t1.name = "666"       # 给线程的name属性赋值
    # print(t1.getName())   # getName() 获取对象的名字
    # print(t1.isAlive())   # 判断是否还运行
    # t1.join()
    print("线程")
第一种创建方式
class MyThread(Thread):     # 创建一个线程类,  和创建进程一样

    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print(f"{self.name}:hah")

if __name__ == '__main__':
    t = MyThread("赏金猎人")
    t.start()

    print("线程")
第二种创建方式

3.线程中的几个方法

  start():启动线程

  join():等待该线程结束,再往下执行

  is_alive():查看线程是否还在运行

  getName():获取线程的名称

4.线程中的几个属性

  name:线程的名称,可以自定义

  daemon:守护线程,默认为False

二.线程是共享这一进程中的数据

  线程存在于同一个进程中,所以线程是共享同一进程里的数据,但是多个线程内部都有自己的数据栈,数据不共享.

from threading import Thread
import time,os

num = 100
def f1():
    time.sleep(1)
    global num
    num = 3
    print(os.getpid())
    print(num)

if __name__ == '__main__':
    t = Thread(target=f1,)
    t.start()
    t.join()        # 等待线程代码执行完再往下执行
    print(os.getpid())
    print(num)
验证线程中的数据是否共享

三.线程与进程效率的对比

1.线程比进程开销小,因为进程需要开辟内存,而线程是在进程中的.

2.由于cpython解释器的原因,线程不能应用多核.

from multiprocessing import Process,Pool
from threading import Thread
import time

def f1():
    pass

if __name__ == '__main__':

    t_start_time = time.time()
    t_lst = []
    for i in range(8):
        t = Thread(target=f1,)
        t.start()
        t_lst.append(t)
    [tt.join() for tt in t_lst]

    t_end_time = time.time()
    t_res_time = t_end_time - t_start_time

    p_start_time = time.time()
    p_lst = []
    for i in range(8):
        p = Process(target=f1, )
        p.start()
        t_lst.append(p)
    [pp.join() for pp in p_lst]

    p_end_time = time.time()
    p_res_time = p_end_time - p_start_time

    print("多线程:",t_res_time)
    print("多进程:",p_res_time)
线程和进程创建的效率的对比

四.守护线程和守护进程

1.线程的结束方式:等所有线程都结束了主线程再结束.

  守护线程:其他非守护线程结束,守护线程也随之结束

2.进程的结束方式:主进程代码先执行完,但是主进程需要等待所有的进程结束后(回收,否则子进程就成了僵尸进程)才结束.

  守护进程:随着主进程的结束而结束

五.锁

1.线程锁(互斥锁/同步锁)

  线程锁:锁定一段代码,只能有一个线程执行.保护数据安全

from threading import Thread , Lock
import time

num = 100
def f1(loc):

    loc.acquire()
    global num
    temp = num
    temp -= 1
    time.sleep(0.1)
    num = temp
    loc.release()

if __name__ == '__main__':
    l = Lock()
    t_lst = []
    for i in range(10):
        t = Thread(target=f1,args=(l,))
        t.start()
        t_lst.append(t)
    [tt.join() for tt in t_lst]

    print(num)
线程锁

2.死锁现象

  现象:出现在锁嵌套的时候,两个线程互相抢对方已经获取的锁,双方互相等待,就造成了死锁现象

from threading import Thread , Lock , RLock
import time

def f1(locA,locB):

    locA.acquire()
    print("f1抢到A锁")
    time.sleep(1)       # 停一秒,确保f2把B锁抢到

    locB.acquire()
    print("f1抢到B锁")
    locB.release()
    locA.release()

def f2(locA,locB):

    locB.acquire()
    print("f2抢到了B锁")
    time.sleep(1)       # 停一秒,确保f1把A锁抢到

    locA.acquire()
    print("f2抢到了A锁")
    locA.release()
    locB.release()

if __name__ == '__main__':
    locA = Lock()
    locB = Lock()

    t1 = Thread(target=f1,args=(locA,locB))
    t2 = Thread(target=f2,args=(locA,locB))

    t1.start()
    t2.start()
死锁现象

3.递归锁(RLock)

  递归锁:递归锁要使用同一把锁,只是锁名不一样.内部类似有一个计数器,获取锁的时候+1,释放锁的时候-1,为0的时候别的线程就能抢这把锁.能够解决死锁现象

from threading import Thread , Lock , RLock
import time

def f1(locA,locB):

    locA.acquire()
    print("f1抢到A锁")
    time.sleep(1)       # 停一秒,确保f2把B锁抢到

    locB.acquire()
    print("f1抢到B锁")
    locB.release()
    locA.release()

def f2(locA,locB):

    locB.acquire()
    print("f2抢到了B锁")
    time.sleep(1)       # 停一秒,确保f1把A锁抢到

    locA.acquire()
    print("f2抢到了A锁")
    locA.release()
    locB.release()

if __name__ == '__main__':
    locA = locB = RLock()   # 必须是同一把递归锁,递归锁内部计数来操作什么时候释放锁

    t1 = Thread(target=f1,args=(locA,locB))
    t2 = Thread(target=f2,args=(locA,locB))

    t1.start()
    t2.start()

"""
lockA = lockB = RLock()     创建一个递归锁,一定要同一个锁,不同变量名
lockA.acquire()         一次内部计数器 +1
lockA.release()         一次内部计数器 -1   为0的时候,别的线程就能抢锁了
"""
递归锁解决死锁现象

4.GIL(Global Interpreter Lock )全局解释器锁

  GIL锁:是在cpython解释器中的一把互斥锁.

   由于GIL锁的存在,线程不能应用多核,使得python在处理计算密集型的线程效率会很低.

  

六.信号量

  和进程中的信号量一样

七.事件

  和进程中的事件一样

原文地址:https://www.cnblogs.com/q767498226/p/10260668.html