Python 线程同步 详解

1、概念

线程同步,线程间协同,通过某种计数,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完成对数据的操作。

2、Event

Event事件,是线程间通信机制中最简单的实现,使用一个内部的标记flag,通过flag的True或者False的变化来进行操作。

如果使用同一个Event对象的标记Flag。谁wait就是等到flag变为True,或等到超时返回False。不限制等待的个数。

名称 含义
set() 标记设置为True
clear() 标记设置为Flase
is_set() 标记 是否 为True
wait(timeout=None) 设置等待标记为True的时长,None为无限等待。等到返回True,未等到超时了返回False

2.1 示例 1

import threading
import time
import logging


FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)

def worker(e:threading.Event):
    logging.info('Before time.sleep(5). I am in thread-{}.'.format(threading.current_thread()))
    time.sleep(5)
    logging.info('After time.sleep(5). I am in thread-{}.'.format(threading.current_thread()))
    e.set()

event = threading.Event()
logging.info(type(event))
logging.info("{} {}".format(event, event.is_set()))
event.set()
logging.info("{} {}".format(event, event.is_set()))
event.clear()
logging.info("{} {}".format(event, event.is_set()))
logging.info('Start the worker_thread.')
threading.Thread(target=worker, args=(event, ), name='worker').start()
logging.info('Before the event.wait(timeout=2)')
event.wait(timeout=2)  # 阻塞等待,等待时间为2s
logging.info('After the event.wait(timeout=2)')
logging.info("{} {}".format(event, event.is_set()))
logging.info('Before the event.wait()')
event.wait()  # 阻塞等待
logging.info('After the event.wait()')
logging.info("{} {}".format(event, event.is_set()))
2022-04-20 14:35:42,749 MainThread    41596 <class 'threading.Event'>
2022-04-20 14:35:42,750 MainThread    41596 <threading.Event object at 0x000001DF7A6CEE50> False
2022-04-20 14:35:42,750 MainThread    41596 <threading.Event object at 0x000001DF7A6CEE50> True
2022-04-20 14:35:42,750 MainThread    41596 <threading.Event object at 0x000001DF7A6CEE50> False
2022-04-20 14:35:42,750 MainThread    41596 Start the worker_thread.
2022-04-20 14:35:42,750     worker    47132 Before time.sleep(5). I am in thread-<Thread(worker, started 47132)>.
2022-04-20 14:35:42,750 MainThread    41596 Before the event.wait(timeout=2)
2022-04-20 14:35:44,760 MainThread    41596 After the event.wait(timeout=2)
2022-04-20 14:35:44,760 MainThread    41596 <threading.Event object at 0x000001DF7A6CEE50> False
2022-04-20 14:35:44,760 MainThread    41596 Before the event.wait()
2022-04-20 14:35:47,753     worker    47132 After time.sleep(5). I am in thread-<Thread(worker, started 47132)>.
2022-04-20 14:35:47,753 MainThread    41596 After the event.wait()
2022-04-20 14:35:47,753 MainThread    41596 <threading.Event object at 0x000001DF7A6CEE50> True

Process finished with exit code 0

2.2 示例 2

# 老板雇佣了一个工人,让他生产被杯子,老板一直等着这个工人,直到生产了5个杯子
import threading
import time
import logging


FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)

event = threading.Event()

def worker(e:threading.Event, n:int):
    i = 0
    while True:
        time.sleep(1)
        i += 1
        logging.info('I am working. I\'ve already made {} cups.'.format(i) )
        if i >= n:
            event.set()
            logging.info('Job done.')
            break

def boss(e:threading.Event):
    while not event.is_set():
        logging.info('I am waiting.')
        time.sleep(1)
    logging.info('Good job.')

threading.Thread(target=boss, args=(event, ), name='boss').start()
threading.Thread(target=worker, args=(event, 5), name='worker').start()
2022-04-20 15:17:43,252       boss    48612 I am waiting.
2022-04-20 15:17:44,260     worker    13684 I am working. I've already made 1 cups.
2022-04-20 15:17:44,260       boss    48612 I am waiting.
2022-04-20 15:17:45,273     worker    13684 I am working. I've already made 2 cups.
2022-04-20 15:17:45,273       boss    48612 I am waiting.
2022-04-20 15:17:46,275       boss    48612 I am waiting.
2022-04-20 15:17:46,276     worker    13684 I am working. I've already made 3 cups.
2022-04-20 15:17:47,280     worker    13684 I am working. I've already made 4 cups.
2022-04-20 15:17:47,280       boss    48612 I am waiting.
2022-04-20 15:17:48,288     worker    13684 I am working. I've already made 5 cups.
2022-04-20 15:17:48,288     worker    13684 Job done.
2022-04-20 15:17:48,288       boss    48612 Good job.

Process finished with exit code 0

2.3 示例 3

# 老板雇佣了一个工人,让他生产被杯子,老板一直等着这个工人,直到生产了5个杯子
import threading
import time
import logging


FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)

event = threading.Event()

def worker(e:threading.Event, n:int):
    i = 0
    while True:
        time.sleep(1)
        i += 1
        logging.info('I am working. I\'ve already made {} cups.'.format(i) )
        if i >= n:
            event.set()
            logging.info('Job done.')
            break

def boss(e:threading.Event):
    logging.info('I am waiting.')
    e.wait()  # 阻塞等待
    logging.info('Good job.')

threading.Thread(target=boss, args=(event, ), name='boss').start()
threading.Thread(target=worker, args=(event, 5), name='worker').start()
2022-04-20 15:19:27,761       boss    36424 I am waiting.
2022-04-20 15:19:28,771     worker    35036 I am working. I've already made 1 cups.
2022-04-20 15:19:29,782     worker    35036 I am working. I've already made 2 cups.
2022-04-20 15:19:30,791     worker    35036 I am working. I've already made 3 cups.
2022-04-20 15:19:31,799     worker    35036 I am working. I've already made 4 cups.
2022-04-20 15:19:32,802     worker    35036 I am working. I've already made 5 cups.
2022-04-20 15:19:32,802     worker    35036 Job done.
2022-04-20 15:19:32,802       boss    36424 Good job.

Process finished with exit code 0

2.4 示例 4

import threading
import logging

FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)

def worker(e:threading.Event, interval:int):
    while not e.wait(interval):
        logging.info('do sth.')

event = threading.Event()

threading.Thread(target=worker, args=(event, 1), name='worker').start()

event.wait(5)
event.set()

logging.info('Main Thread End.')
2022-04-20 15:34:55,823     worker    13952 do sth.
2022-04-20 15:34:56,830     worker    13952 do sth.
2022-04-20 15:34:57,838     worker    13952 do sth.
2022-04-20 15:34:58,842     worker    13952 do sth.
2022-04-20 15:34:59,820 MainThread    41196 Main Thread End.

Process finished with exit code 0

3、threading.Timer

threading.Timer继承自Thread,这个类用来定义延迟多久后执行一个函数。

threading.Timer(self, interval, function, args=None, kwargs=None)start方法执行之后,Timer对象会处于等待状态,等待了interval秒之后,开始执行function函数。但是,只要start以后,工作线程就启动了,只不过是延迟了interval后,才执行函数的。

  • Timer是线程Thread的子类,就是线程类,具有线程的能力和特征
  • 它的实例是能够延时执行目标函数的进程,在真正执行目标函数之前,都可以cancel它。
  • cancel方法本质使用Event类实现。这并不是说,线程提供了取消的方法。

3.1 示例 1

import threading
import logging
import time


FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)


def worker():
    logging.info('in worker - beginning')
    time.sleep(2)
    logging.info('in worker - ending')

t = threading.Timer(4, worker)
t.setName('timer')

t.start()

while True:
    time.sleep(1)
    logging.info(threading.enumerate())
    if threading.active_count() == 1:
        break
2022-04-20 15:56:19,392 MainThread    42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:20,403 MainThread    42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:21,415 MainThread    42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:22,389      timer    39616 in worker - beginning
2022-04-20 15:56:22,421 MainThread    42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:23,429 MainThread    42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:24,391      t
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值