文档章节

python3.6 - threading 多线程编程进阶,线程间并发控制(2)

 东昕
发布于 2017/07/31 11:02
字数 1217
阅读 59
收藏 0

1. 线程间同步简介

  • Condition 条件变量
  • Semaphore 信号量
  • Barrier 栅栏/屏障
  • Timer 定时器

2. Condition 条件变量

定义

条件变量变量是来自 POSIX 线程标准的一种线程同步机制。主要用来等待某个条件的发生,用来同步同一进程中的多个线程。 条件变量总是和一个互斥量或锁关联,条件本身由互斥量保护,线程在改变条件状态时必须锁住互斥量。 线程可以等待在某个条件上,不需要通过轮询的方式来判断,节省CPU时间

使用方法

  • 条件变量需要配合着锁(或者互斥量)一起使用,条件本身接受锁的保护,控制线程的资源访问
  • 条件变量使用 with 语法自动调用关联锁的获取(acquire),释放(release)方法
  • wait 方法会释放锁,并且阻塞当前线程,直到被其他线程的 notify/notify_all 唤醒,一旦阻塞的线程被从 wait 唤醒,将重新获取锁
  • 用 notify 方法唤醒等待条件变量的一个线程,notify_all 方法唤醒等待条件变量的所有线程
  • notify 和 notify_all 方法不会释放锁,也就是说被唤醒的线程不会立即从 wait 方法返回,需要调用 notify 和 notify_all 的线程交出锁的所有权才能返回

使用示例

生产者,消费者模型,使用条件变量控制并发访问普通的 list

#!/usr/bin/env python
# coding: utf-8


import time
import threading
import random
import itertools


# 全局变量
# 停止标志
is_stop = False

# 队列最大值
MAX_NUM = 100

# 队列对象
data_queue = []


class Producer(threading.Thread):
    '''
    生产者线程对象
    name: 线程名字
    condition: 条件变量
    '''
    def __init__(self, name, condition):
        self._oname = name
        self._condition = condition
        super().__init__()

    def run(self):
        '''
        生产过程
        '''
        while True:
            with self._condition:
                while len(data_queue) >= MAX_NUM:
                    print('Producer oname={}, to wait'.format(self._oname))
                    self._condition.wait()

                # 判断是否结束生产
                if is_stop:
                    return

                data_queue.append('Producer oname={}, value={}'
                                  .format(self._oname, random.random()))
                info = 'Producer oname={}, queue_size={}' \
                        .format(self._oname, len(data_queue))
                print(info)
                self._condition.notify()
            time.sleep(random.random())


class Consumer(threading.Thread):
    '''
    消费者线程对象
    name: 线程名字
    condition: 条件变量
    '''
    def __init__(self, name, condition):
        self._oname = name
        self._condition = condition
        super().__init__()

    def run(self):
        '''
        消费过程
        '''
        while True:
            with self._condition:
                while not data_queue:
                    print('    Consumer oname={}, to wait'
                          .format(self._oname))

                    # 判断是否结束消费
                    if is_stop:
                        return

                    self._condition.wait()
                data = data_queue.pop(0)
                size = len(data_queue)
                self._condition.notify()

            print('    Consumer oname={}, data={}, queue_size={}'
                  .format(self._oname, data, size))
            time.sleep(random.random())


def main():
    '''
    验证函数
    '''
    # 创建条件变量,供生产者线程,消费者线程使用
    condition = threading.Condition()

    # 创建生产者线程组
    producerts_thr = [Producer('Producer_{}'.format(i), condition)
                      for i in range(4)]
    # 创建消费者线程组
    consumers_thr = [Consumer('Consumer_{}'.format(i), condition)
                     for i in range(4)]

    # 开始生产者,消费者线程
    [i.start() for i in itertools.chain(producerts_thr, consumers_thr)]

    # 定义停止计时器函数
    def stop_all():
        with condition:
            global is_stop
            is_stop = True

    # 设定运行 10 秒钟之后停止生产者,消费者线程
    t = threading.Timer(10.0, stop_all)
    t.start()

    # 等待生产者,消费者线程结束
    [i.join() for i in itertools.chain(producerts_thr, consumers_thr)]


if __name__ == '__main__':
    main()

3. Semaphore 信号量

定义

信号量管理了一个内部计数器,用 acquire 方法减少计数,release 方法增加计数,计数器不能小于 0, 当 acquire 发现计数器是 0 的时候,这个方法会阻塞,直到其他的线程调用 release 增加计数

使用方法

  • acquire 减少计数,计数器是 0 的时候阻塞
  • release 增加计数

使用示例

#!/usr/bin/env python
# coding: utf-8


import threading
import random
import time


def work_thr(pool_sema, num):
    '''
    线程函数
    '''
    while True:
        # 每次只允许固定数目的线程进入
        with pool_sema:
            print('thr num={}'.format(num))

        time.sleep(3)


if __name__ == '__main__':
    # 创建允许 5 个线程访问的 semaphore
    pool_sema = threading.BoundedSemaphore(5)

    # 创建 20 个线程
    thrs = [threading.Thread(target=work_thr, args=[pool_sema, i])
            for i in range(20)]

    # 开始所有线程
    [thr.start() for thr in thrs]

    # 等待线程结束
    [thr.join() for thr in thrs]

4. Barrier 栅栏/屏障

定义

Barrier 提供了一种同步方法,配置一个数值,当到达 wait 阻塞点的线程数到达配置的值时(或者说调用 wait 线程数大于等于配置值)再向后面执行

使用示例(伪代码)

同步客户端和服务器的例子

#!/usr/bin/env python
# coding: utf-8


import time
from threading import Barrier, Thread


b = Barrier(2, timeout=5)

def server():
    # start_server()
    time.sleep(3)

    # 服务端到达
    b.wait()
    print('start server')

def client():
    time.sleep(5)
    # 客户端到达
    b.wait()
    print('start client')

if __name__ == '__main__':
    # 创建线程
    serv_thr = Thread(target=server)
    cli_thr = Thread(target=client)

    # 开始执行线程
    serv_thr.start()
    cli_thr.start()

    # 等待线程退出
    serv_thr.join()
    cli_thr.join()

5. Timer 定时器

定义

基于线程提供一个定时器,定时执行函数

使用示例

def hello():
    print("hello, world")

# 30 秒后打印 "hello, world"
t = Timer(30.0, hello)
t.start()

本文转载自:https://www.u3v3.com/ar/1383

粉丝 12
博文 22
码字总数 16965
作品 0
浦东
架构师
私信 提问
Python 线程、线程通信、多线程

这是一篇学习Python 线程相关的内容,记录一下以备复习和开发使用,技术有限,如有问题欢迎指出,多谢。 一.GIL 全局解释器锁(cpython) 1.为什么会有这个锁:为了线程安全,减少python使用者...

rieuse
2018/09/18
0
0
python --- 基础多线程编程

在python中进行多线程编程之前必须了解的问题: 1. 什么是线程?   答:线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位...

码农47
2017/10/27
0
0
Python 多线程和锁

Python 多线程和锁 作者博客:http://zzir.cn 进程和线程 进程是执行中的计算机程序。每个进程都拥有自己的地址空间、内存、数据栈及其它的辅助数据。操作系统管理着所有的进程,并为这些进程...

lalamina
2016/09/06
1K
0
Python核心编程:第18章 多线程编程

18.1 引言/动机 1.多线程编程对于某些任务是最理想的。这些任务具有以下特点:它们本质就是异步的,需要有多个并发事务,各个事务的运行顺序可以是不确定的、随机的、 不可预测的。这样的编程...

finndai
2016/12/08
49
0
进程,线程,超线程,并发,并行 等概念

进程是操作系统对一个正在运行的程序的抽象,即操作系统为该进程虚拟了独自的处理器资源,内存空间(又称虚拟地址空间)与磁盘空间 线程是进程中多个可以派遣的工作单位(或称执行单元,以C...

lvzjane
2012/11/28
973
1

没有更多内容

加载失败,请刷新页面

加载更多

nginx+tomcat配置https

1、nginx配置https和【proxy_set_header X-Forwarded-Proto $scheme;】 2、java代码: String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServe......

perofu
23分钟前
4
0
必看的Linux系统新手进阶老手心得

不知道从什么时候起,linux这个话题变得越来越普及,成为大家经常讨论的话题。无论在网络上还是实际生活中,竟然很多人都在纠结学习linux的问题。网络上给的答案千千万万,而却还有很多人踌躇...

Linux就该这么学
26分钟前
4
0
Spring Boot 配置元数据指南

1. 概览 在编写 Spring Boot 应用程序时,将配置属性映射到 Java bean 上是非常有用的。但是,记录这些属性的最好方法是什么呢? 在本教程中,我们将探讨 Spring Boot Configuration Proces...

liululee
30分钟前
3
0
foreach查找子类

$list = $menu_model -> menu_list();$parent_list = [];foreach ($list as $v){ if ($v['pid'] == 0) { $parent = $v; foreach ($list as $v1) ......

小小小壮
41分钟前
3
0
基于 HTML5 Canvas 实现的 TP-LINK 电信拓扑设备面板

前言 今天我们以真实的 TP-LINK 设备面板为模型,完成设备面板的搭建,和指示灯的闪烁和图元流动。 先来目睹下最终的实现效果:http://www.hightopo.com/demo/blog_tplink_20170511/index.h...

htdaydayup
47分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部