线程

3. 线程

[TOC]

什么是线程

'''
1. 什么是线程
  - CPU的执行单位,相当于车间的流水线
  - 一个进程可以开启多个线程
  - 线程的开销远小于进程
  - 线程不能实现并行,只能实现并发
'''

创建线程的方式

from threading import Thread

# 方式1
def task(name):
    print(f'{name}')

if __name__ == '__main__':
    t=Thread(target=task,args=('wick',))
    t.start()

# 方式2
class MyTask(Thread):
    def __init__(self,name):
        super().__init__()
        self.name = name


    def run(self):
        print(f'{self.name}')
if __name__ == '__main__':
    t = MyTask('wick')
    t.start()

线程对象的属性方法

'''
1. Thread实例对象的方法
  - isAlive(): 返回线程是否活动的
  - getName(): 返回线程名
    - setName(): 设置线程名

2. threading模块提供的一些方法:
  - threading.currentThread(): 返回当前的线程变量。
  - threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  - threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果    
'''
from threading import Thread
import threading


def work():
    import time
    time.sleep(3)
    print(threading.current_thread().name)


if __name__ == '__main__':
    #在主进程下开启线程
    t=Thread(target=work)
    t.start()

    print(t.isAlive())
    print(t.is_alive())
    print(t.getName())


    print(threading.current_thread().name)
    print(threading.current_thread()) #主线程
    print(threading.enumerate()) #连同主线程在内有两个运行的线程
    print(threading.active_count())
    print('主线程/主进程')

守护线程

'''
1. 主线程运行完毕后,产生的所有子线程也会跟着结束
  - 主线程在其他非守护线程运行完毕后才算运行完毕,即主线程会一直等非守护的子线程都运行完毕后才结束

2. 守护线程设置
  - t1.setDaemon(True)
  - t1.daemon = True
'''
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")


t1=Thread(target=foo)
t2=Thread(target=bar)

t2.daemon=True
t1.start()
t2.start()
print("main-------")

# 只有当t1和t2均为守护线程时,才算主线程运行完毕,从而结束子线程,否则,主线程会等待t2结束后才结束,从而杀死t1线程

线程互斥锁

'''
1. 线程互斥锁
  - 线程之间数据共享,当线程并发运行时,会造成数据读取修改错误问题
  - 让并行变串行

2.   mutex = Lock()
  - mutex.acquire()  # 加锁
  - mutex.release()  # 释放锁
'''
import time
from threading import Thread, Lock
n = 100
mutex = Lock()

def task(i):
    print(f'线程{i}启动。。。')
    global n

    mutex.acquire()  # 加锁
    temp = n
    time.sleep(0.1)
    n = temp - 1
    print(n)
    mutex.release()  # 释放锁


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

    for t in t_l:
        t.join()

     print(n)  # 0

GIL全局解释器锁

'''
1. 介绍
  - 一个python的进程内,除了主线程及主线程开启的其他线程外,还有解释器开启的垃圾回收等解释器界别的线程
  - 解释器的代码是所有线程共享
  - 为防止线程1执行类似x=100的代码的同时,垃圾回收线程回收了100的操作,因此通过GIL锁,保证python解释器同一时间只能执行一个任务的代码

2. 结论
  - 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势  
  - GIL本质是互斥锁,是将并行变为串行
  - GIL保护的是解释器级的数据,保证的是线程安全

3. 多线程使用场景
  - 多线程用于IO密集型,如socket、爬虫、web
  - 多进程用于计算密集型,如金融分析,人工智能  
  - 多个IO密集型
    - 使用多进程+多线程  
'''

死锁现象和递归锁(RLock)

'''
1, 死锁现象
  - 两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,称为系统处于死锁状态,或者产生了死锁。

2. 递归锁(解决死锁现象)
  - 内部维护这一个Lock和counter变量
  - acquire一次counter数加1,release一次counter数减1
  - 只有counter为0时,才能被使用
  - 相当于连环锁,要么不用,要么一起拿过去
'''

# 1. 死锁
import time
from threading import Lock,Thread

mutex1 = Lock()
mutex2 = Lock()

def task():
    work1()
    work2()

def work1():
    mutex1.acquire()
    print('拿到锁1')
    mutex2.acquire()
    print('拿到锁2')
    mutex2.release()
    print('解开锁1')
    mutex1.release()
    print('解开锁2')

def work2():
    mutex2.acquire()
    print('拿到锁2')
    time.sleep(1)
    mutex1.acquire()
    print('拿到锁2')
    mutex1.release()
    print('解开锁1')
    mutex2.release()
    print('解开锁2')

for i in range(10):
    t = Thread(target=task)
    t.start()


# 2. 递归锁(RLock)
import time
from threading import RLock,Thread
mutex1 = mutex2= RLock()

def task():
    work1()
    work2()

def work1():
    mutex1.acquire()
    print('拿到锁1')
    mutex2.acquire()
    print('拿到锁2')
    mutex2.release()
    print('解开锁1')
    mutex1.release()
    print('解开锁2')

def work2():
    mutex2.acquire()
    time.sleep(1)
    mutex1.acquire()
    print('拿到锁2')
    mutex1.release()
    print('解开锁1')
    mutex2.release()
    print('解开锁2')

for i in range(10):
    t = Thread(target=task)
    t.start()

信号量(Semaphore)

'''
1. 信号量(也可解决死锁现象)
  - 内置计数器,每当调用acquire计数器-1,调用release内置计数器+1
  - 计数器不能小于0,计数器为0时,acquire会阻塞
  - 相当于锁店
'''
import time
from threading import Lock,Thread,Semaphore

sm = Semaphore(10)

def task():
    work1()
    work2()

def work1():
    sm.acquire()
    print('拿到锁1')
    sm.acquire()
    print('拿到锁2')
    sm.release()
    print('解开锁1')
    sm.release()
    print('解开锁2')

def work2():
    sm.acquire()
    time.sleep(1)
    sm.acquire()
    print('拿到锁2')
    sm.release()
    print('解开锁1')
    sm.release()
    print('解开锁2')

for i in range(10):
    t = Thread(target=task)
    t.start()

Event事件

'''
1. Event事件(线程控制线程的执行)
  - 程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作
  - 当Event对象为False时,该线程会被阻塞,直至Event对象为真,相当于红绿灯的作用

2. 使用方法
  - isSet():返回Event对象的状态
  - wait():判断Event对象的状态,若为Flase,则阻塞
  - Set():设置Event对象为Ture
  - clear():恢复Event对象为False
'''
from threading import Event
from threading import Thread
import time

e = Event()

def light():
    print('红灯亮')
    time.sleep(5)
    print('绿灯亮')
    e.set()

def car():
    print('等红灯中...')
    e.wait()
    print('车辆加速中')


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

for i in range(20):
    a  = Thread(target=car)
    a.start()

posted on 2025-12-05 11:22  wickyo  阅读(0)  评论(0)    收藏  举报