多线程

Posted gredae

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。

线程

什么是线程

程序在运行代码的过程。

进程与线程

进程:一个在运行中的程序(具体内容是由线程来运行)的状态,是系统的一个资源单位。

线程:运行指定代码的过程。一个进程中至少有一个线程,但是一个进程可以有多个线程。线程是cpu调度的最小单位。

类比到现实中就是生产车间(指代进程)与流水线(指代线程)的关系。我们可以说这个车间是生产汽车,生产车间里面的每一条流水线都是生产汽车部件的。生产车间是由一个一个流水线组合成地。进程也是由线程构成地。

线程与进程的区别

  1. 进程之间是内存隔离,线程之间是共享内存
  2. 创建进程由于需要开辟内存空间,而线程不需要开辟内存空间,所以创建线程的速度比创建进程的速度快

线程创建的两种方式

使用threading模块创建Thread,而且创建线程的方式与multiprocessing创建线程的方式一样(据说是multiprocessing照着threading写的,不要说是我说的)

  • 方式一:实现Thread类

    from threading import Thread,currentThread
    
    def pr():
        print(f'我是进程currentThread().name!')
    
    t = Thread(target=pr)
    t.start()
    print('我是主进程!')
    #######################
    # 结果:
    我是进程Thread-1!   # 因为创建线程的速度很快所以创建了线程就执行了代码
    我是主进程!
  • 方式二:继承Thread类

    from threading import Thread,currentThread
    
    class MyT(Thread):
        def run(self) -> None:   # 必须重写run方法
            print(f'我是进程currentThread().name')
    
    t = MyT()
    t.start()
    print('我是主进程!')
    #######################
    # 结果:
    我是进程Thread-1!
    我是主进程!

线程的属性和方法

  • join

    主线程等待子线程运行结束

    from threading import Thread
    import time
    
    def task():
        print('子线程开启!')
        time.sleep(2)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.start()
    print('主线程结束!')
    ######################
    # 结果:
    子线程开启!
    主线程结束!
    子线程结束!
  • isAlive()

    线程是否还在运行

    from threading import Thread
    import time
    
    def task():
        print('子线程开启!')
        time.sleep(2)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.start()
    time.sleep(1)
    print(t.isAlive())   # True
    print('主线程结束')
  • getName()

    获取线程名字

    from threading import Thread
    import time
    
    def task():
        print('子线程开启!')
        time.sleep(2)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.start()
    print(t.getName())   # Thread-1
    print('主线程结束')
  • setName()

    设置线程名字

    from threading import Thread
    import time
    
    def task():
        print('子线程开启!')
        time.sleep(2)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.start()
    print(t.getName())   # Thread-1
    t.setName('张三')
    print(t.getName())   # 张三
    print('主线程结束')
  • currentThread()

    获取当前线程对象

    from threading import Thread,currentThread
    import time
    
    def task():
        print(currentThread().getName())   # Thread-1
        currentThread().setName('张三')
        print(currentThread().getName())   # 张三
    
    t = Thread(target=task)
    t.start()
    time.sleep(0.5)
    print(t.getName())   # 张三
    print('主线程结束')
  • enumerate()

    返回当前活动进程

    from threading import Thread,enumerate
    import time
    
    def task():
        print('子线程开启!')
        time.sleep(2)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.start()
    print(enumerate())   # [<_MainThread(MainThread, started 3068)>, <Thread(Thread-1, started 16348)>]
    print('主线程结束')
  • activeCount()

    返回当前活动线程数量

    from threading import Thread,activeCount
    import time
    
    def task():
        print('子线程开启!')
        time.sleep(2)
        print('子线程结束!')
    
    t = Thread(target=task)
    t.start()
    print(activeCount())   # 2
    print('主线程结束')

守护线程

守护线程守护至当前进程结束

from threading import Thread
import time

def task():
    print('子线程开启!')
    time.sleep(2)
    print('子线程结束!')

t = Thread(target=task)
t.daemon = True
t.start()

print('主线程结束')

但是,主进程会等待还没有结束的子线程结束后结束

from threading import Thread
import time

def task():
    print('守护线程开启!')
    time.sleep(2)
    print('守护线程结束!')

def pr():
    print('子线程开启!')
    time.sleep(3)
    print('子线程结束!')

t = Thread(target=task)
t.daemon = True
t.start()
t = Thread(target=pr)
t.start()

print('主线程结束')

线程锁

当我们要对线程间共享的资源进行操作,为了资源的安全考虑,就需要为资源加上线程锁。这样就可以保证同一个资源同一时刻只有一个线程有权限去修改。

from threading import Lock,Thread
import time
#初始化一把锁,由于线程间共享内存所以可以不用传递参数
lock=Lock()
count = 1

def buy():
    #获取钥匙在acquire()和release()之间的代码在同一时间只允许一个进程执行,当一个进程完成后将钥匙归还后,其他的进程才可以获得钥匙。
    lock.acquire()  # 拿锁
    # 模拟网络的延迟
    global count
    time.sleep(0.1)
    if count>0:
        print('买到票了')
        count=count-1
        with open('access.txt', 'w', encoding='utf-8') as f:
            f.write(str(count))
    else:
        print('没票了')
    lock.release()  #还锁

count = 1
for i in range(5):
    p=Thread(target=buy)
    p.start()

死锁

当程序中有两把锁,并且一个线程需要拿到两把锁才能运行下去的时候,就会发生死锁的问题

from threading import Lock,Thread,currentThread
import time

lock1 = Lock()
lock2 = Lock()

def func1():
    lock2.acquire()
    print('\033[41m%s 拿到A锁\033[0m' % currentThread().name)

    lock1.acquire()
    print('\033[42m%s 拿到B锁\033[0m' % currentThread().name)
    lock1.release()

    lock2.release()

def func2():
    lock1.acquire()
    print('\033[43m%s 拿到B锁\033[0m' % currentThread().name)
    time.sleep(2)

    lock2.acquire()
    print('\033[44m%s 拿到A锁\033[0m' % currentThread().name)
    lock2.release()

    lock1.release()

t1 = Thread(target=func2)
t2 = Thread(target=func1)
t1.start()
t2.start()

解决死锁问题可以使用 递归锁

递归锁

在一个线程中是的锁可以被多次获取,但是不同线程间只能有一个线程获得锁

from threading import RLock,Thread,currentThread
import time

lock1 = RLock()
lock2 = lock1

def func1():
    lock2.acquire()
    print('\033[41m%s 拿到A锁\033[0m' % currentThread().name)

    lock1.acquire()
    print('\033[42m%s 拿到B锁\033[0m' % currentThread().name)
    lock1.release()

    lock2.release()

def func2():
    lock1.acquire()
    print('\033[43m%s 拿到B锁\033[0m' % currentThread().name)
    time.sleep(2)

    lock2.acquire()
    print('\033[44m%s 拿到A锁\033[0m' % currentThread().name)
    lock2.release()

    lock1.release()

t1 = Thread(target=func2)
t2 = Thread(target=func1)
t1.start()
t2.start()

GIL(全局解释器锁)

因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁来保证同一时间只有一个线程在解释器中运行。但是这会导致cpython中的多线程并不是真正意义上的多线程,它只能运用到单核的性能。

解决这个问题主要有两种办法:

  1. 使用多线程
  2. 使用C语言写多线程的部分,再使用ctypes模块

具体可以看:https://blog.csdn.net/IAlexanderI/article/details/72932309

线程间通信

  1. 队列

    先进先出

    import queue
    q = queue.Queue()
    q.put('123')
    q.put('qweqwe')
    print(q.get())
    print(q.get())
    ###############
    # 结果:
    123
    qweqwe
  2. 后进先出

    import queue
    q = queue.LifoQueue()
    q.put('123')
    q.put('qwe')
    q.put('asd')
    print(q.get())
    print(q.get())
    print(q.get())
    ################
    # 结果:
    asd
    qwe
    123
  3. 优先级

    按照元组中的优先级输出

    import queue
    q = queue.PriorityQueue()
    q.put((4,'123'))
    q.put((2,'qwe'))
    q.put((3,'asd'))
    print(q.get())
    print(q.get())
    print(q.get())
    ################
    # 结果:
    (2, 'qwe')
    (3, 'asd')
    (4, '123')

线程定时器

倒计时,当时间归零开始执行线程

from threading import Timer

def task():
    print('线程开始执行')

t = Timer(4,task) # 过了4s后开启了一个线程
t.start()

以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

什么是多线程,多进程?

多线程和多进程模式有啥区别

多线程Java多线程学习笔记 | 多线程基础知识

java中啥叫做线程?啥叫多线程?多线程的特点是啥

c++ 多线程与c多线程有啥区别?

IOS多线程安全(线程锁)