Python 多线程、守护进程、同时运行最大线程数、锁、线程阻塞(线程暂停和继续)

python 多线程的使用笔记

1、多线程的基本用法

(1)简单任务多线程的开启方式

from threading import Thread
import time

def target(name, n):
    for i in range(0, n, 2):
        print(f'{name}_{i}')
        time.sleep(1)

t1 = Thread(target=target, args=('a', 16, ))
t1.start()

for i in range(1, 11, 2):
    print(f'主线程_{i}')
    time.sleep(1)

(2)自定义复杂任务多线程的开启方式,适合复杂逻辑

from threading import Thread
import time


class myThread(Thread):
    def __init__(self, name, n):
        super(myThread, self).__init__()
        self.name = name
        self.n = n

    def run(self):
        for i in range(0, self.n, 2):
            print(f'{self.name}_{i}')
            time.sleep(1)


t1 = myThread('a', 16, )
t1.start()

for i in range(1, 11, 2):
    print(f'主线程_{i}')
    time.sleep(1)

2、关于守护线程和 join

正常情况下主线程和子线程可以相互没有干扰,独立运行完后程序结束

当我们需要主线程结束后子线程也随着结束,可以将子线程设置为守护线程后,这样主线程结束后,即便子线程没有运行完,子线程也会随着主线程结束
这里注意的是,子线程设置守护线程一定要在子线程开始之前

当我们需要子线程运行完主线程再接着往下运行时,子线程可以调用 join() 方法, 主线程就会等待,直到子线程终止或者超时才会继续往下执行
这里注意的是,子线程 join() 一定要在子线程开始之后
jion() 里面可以传入 timeout 参数,超时后主线程会继续往下运行
下面是 setDaemo()join() 的例子 可以去掉注释看看有什么不同

from threading import Thread
import time


def target(name, n):
    for i in range(0, n, 2):
        print(f'{name}_{i}')
        time.sleep(1)


t1 = Thread(target=target, args=('a', 16,))
# t1.setDaemon(True)
t1.start()
# t1.join()

for i in range(1, 11, 2):
    print(f'主线程_{i}')
    time.sleep(1)

3、关于信号量 Semaphore()BoundedSemaphore()

我们可以一次开启很多线程,但是我们想对同时运行的线程控制在一定数量内,开启的线程大于同时运行的线程时,其他的线程就处于阻塞状态,直到有线程运行结束,阻塞的线程才能开始运行,这时我们就用到 Semaphore() ,
信号量基于内部计数器,初始化 Semaphore() 时会给计数器一个值,每调用一次acquire(),计数器减1,每调用一次release(), 计数器加1,当计数器为0时,acquire()调用被阻塞

from threading import Thread, Semaphore
import time

sema = Semaphore(3)


def target(name, n):
    sema.acquire()
    for i in range(0, n, 2):
        print(f'{name}_{i}')
        time.sleep(1)
    sema.release()


sema = Semaphore(3)

for s in 'abcde':
    Thread(target=target, args=(s, 6,)).start()

Semaphore() 支持上下文,对于上面代码,也可以这样写

from threading import Thread, Semaphore
import time

sema = Semaphore(3)


def target(name, n):
    with sema:
        for i in range(0, n, 2):
            print(f'{name}_{i}')
            time.sleep(1)


sema = Semaphore(3)

for s in 'abcde':
    Thread(target=target, args=(s, 6,)).start()

BoundedSemaphore 继承自 Semaphore 功能和 Semaphore基本一样,只不过 BoundedSemaphore有限制,release()时会判断当前信号量的值,如果当前值大于等于初始值,就会抛出错误,而Semaphore并不会抛出异常

4 锁 LockRLock

如果运行上面代码,我们会发现,多个子线程之间可能打印在一行上,比较混乱,这是由于线程之间随机调度,同时输出。如果是同时在修改一个变量。或者一个文件,就有可能造成数据错误,这时互斥锁Lock就派上用场了。Lock可以用于锁定资源,Lock有两个基本方法,acquire()release(),调用acquire()状态变为锁定并立即返回,当状态是锁定时, acquire()将阻塞至其他线程调用 release() 将其改为非锁定状态,然后 acquire() 调用重置其为锁定状态并返回。锁支持上下文管理协议,即支持with语

from threading import Thread, Lock
import time

lock = Lock()


def target(name, n):
    for i in range(0, n, 2):
        lock.acquire()
        print(f'{name}_{i}')
        lock.release()
        time.sleep(1)


for s in 'abcde':
    Thread(target=target, args=(s, 6,)).start()

with语句代替

from threading import Thread, Lock
import time

lock = Lock()


def target(name, n):
    for i in range(0, n, 2):
        with lock:
            print(f'{name}_{i}')
        time.sleep(1)


for s in 'abcde':
    Thread(target=target, args=(s, 6,)).start()

如果尝试释放一个非锁定的锁,则会引发 RuntimeError 异常
RLock被称为重入锁,若要锁定锁,线程调用其 acquire() 方法;一旦线程拥有了锁,方法将返回。若要解锁,线程调用 release() 方法。 acquire()/release() 对可以嵌套,重入锁必须由获取它的线程释放。一旦线程获得了重入锁,同一个线程再次获取它将不阻塞。只有最终release() (最外面一对的 release() ) 将锁解开,才能让其他线程继续处理 acquire()阻塞,线程必须在每次获取它时释放一次。
LockRLock 用法大体相同,区别在于Lock在锁定时不属于特定线程,也就是说,Lock可以在一个线程中上锁,在另一个线程中解锁。而对于RLock来说,只有当前线程才能释放本线程上的锁,即解铃还须系铃人。

5 Event 事件

Event内部定义了一个标志位flag,初始的时候为False,当Flag的值为False,那么event.wait()就会阻塞,当flag值为True,那么event.wait()便不再阻塞Event主要提供以下方法
set()来将其设置为True
clear()将其重新设置为False
is_set()来检查标志位的状态;
wait(timeout=None),会一直监听flag,如果flagFalse就一直处于阻塞状态

from threading import Thread, Event
import time

class myThread(Thread):
    def __init__(self, name, n):
        super(myThread, self).__init__()
        self.name = name
        self.n = n
        self.e = Event()

    def pause(self): # 暂停
        self.e.clear()

    def play(self): # 继续
        self.e.set()

    def run(self):
        for i in range(0, self.n, 2):
            self.e.wait()
            print(f'{self.name}_{i}')
            time.sleep(1)

t1 = myThread('a', 16, )
t1.play()
t1.start()

for i in range(1, 11, 2):
    print(f'主线程_{i}')
    time.sleep(1)
    if i == 3:
        t1.pause() # 子线程暂停
    if i == 7:
        t1.play() # 子线程继续

特别感谢下面两篇文章的作者,供大家做参考学习
python多线程详解(超详细)
python threading模块的Lock和RLock区别

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

viziviuz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值