多线程编程

1、多线程对于具有如下特点的编程任务是非常理想的:1、本质上是异步的 2、需要多个并发活动 3、每个活动的处理顺序是不确定的。

2、使用多线程编程,以及类似Queue的共享数据结构,这个编程任务可以规划成几个执行特定函数的线程。

  • UserRequestThread:负责读取客户端输入,该输入可能来自I/O通道。程序将创建多个线程,每个客户端一个,客户端的请求会被放入队列中。
  • RequestProcessor:该线程负责从队列中获取请求并进行处理,为第三个线程提供输出。
  • ReplyThread:负责向用户输出,将结果传回给用户(如果是网络应用),或者把数据写到本地文件系统或数据库中。

  使用多线程来规划这种编程任务可以降低程序的复杂性,使其实现更加清晰、高效和简洁。

3、进程

  计算机程序只是存储在磁盘上的可执行二进制(或其他类型)文件。只有把它们加载到内存中并被操作系统调用,才能拥有其生命周期。进程则是一个执行中的程序。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于追踪执行的辅助数据。操作系统管理其上所有进程的执行,并为这些进程合理的分配时间。进程有可以通过派生(fork或spawn)新的进程来执行其他任务,不过因为每个新进程也拥有自己的内存和数据栈等,所以只能采用进程间通信(IPC)的方式共享信息。

4、线程

  与进程类似,不过它们是在同一个进程下执行的,并共享相同的上下文。可以将它们认为是在一个主进程或“主线程”中并行运行的一些“迷你进程”。

  线程包括开始、执行顺序和结束三个部分。它有一个指令指针,用于记录当前运动的上下文。当其他线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)——这种叫法叫做让步。

5、线程和Python

1、全局解释器

  Python代码的执行是由Python虚拟机(又名解释器主循环)进行控制的。尽管Python解释器中可以运行多个线程,但是在任意时刻只有一个线程会被解释器执行。

  对Python虚拟机的访问是由全局解释器锁(GIL)控制的。这个锁就是用来保证同时只能有一个线程运行。在多线程环境中,Python虚拟机将按照下面所述的方式执行。

  1. 设置GIL
  2. 切换进一个线程去进行
  3. 执行操作之一(a。指定数量的字节码指令  b。线程主动让出控制权(可以调用time.sleep()来完成))
  4. 把线程设置回睡眠状态(切换出线程)
  5. 解锁GIL
  6. 重复上述步骤

I/O密集型的Python程序要比计算密集型的代码能够更好的利用多线程环境。

2、不使用线程的情况

#!/usr/bin/env python

from time import sleep , ctime

def loop0():
    print('start loop 0 at:',ctime())
    sleep(4)
    print('loop 0 done at:',ctime())\

def loop1():
    print('start loop 1 at:',ctime())
    sleep(2)
    print('loop 1 done at:',ctime())

def main():
    print('starting at:',ctime())
    loop0()
    loop1()
    print('all done at:',ctime())

if __name__ == '__main__':
    main()


结果:
starting at: Fri Oct 20 12:09:34 2017
start loop 0 at: Fri Oct 20 12:09:34 2017
loop 0 done at: Fri Oct 20 12:09:38 2017
start loop 1 at: Fri Oct 20 12:09:38 2017
loop 1 done at: Fri Oct 20 12:09:40 2017
all done at: Fri Oct 20 12:09:40 2017

Process finished with exit code 0

3、Python的threading模块

threading模块提供了相比于thread更高级、功能更全面的线程管理

6、thread模块

  除了派生线程外,thread模块还提供了基本的同步数据结构,称为锁对象(lock object,也叫原语锁、简单锁、互斥锁、互斥和二进制信号量)

函数/方法

描述

thread模块的函数

start_new_thread

派生一个新的线程,使用给定的args和可选的kwargs来执行function

allocate_lock()

分配LockType锁对象

exit()

给线程退出命令

LockType锁对象的方法

acquire(wait=None)

尝试获取锁对象

locked()

如果获取锁对象则返回True,否则,返回False

release()

释放锁

7、Threading模块

    表 threading模块的对象

对象

描述

Thread

表示一个执行线程的对象

Lock

锁原语对象(和thread模块中的锁一样)

RLock

可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁)

Condition

条件变量对象,使得一个线程等待另一个线程满足特定的条件

Event

条件变量的通用版本,任意数量的线程等待某个时间的发生,在该事件后所有线程将被激活

Semaphore

与线程共享的有限资源提供一个计数器,如果没有可用资源时会被阻塞。

Timer

与Thread相似,不过它要在运行前等待一段时间

避免使用thread模块的另一个原因是该模块不支持守护进程这个概念。当主线程退出时,所有子线程都将结束,不管它们是否仍在工作。

Threading模块支持守护进程,其工作模式是:守护进程一般是一个等待客户端请求的服务器。如果没有客户端请求,守护进程就是空闲的。如果把一个线程设置为守护进程,就表示这个线程是不重要的,线程退出时不需要等待这个线程执行完成。

要将一个线程设置为守护进程,需要启动线程之前执行如下的赋值语句:thread.daemon = True

1、Thread类

表:Thread对象的属性和方法

属性

描述

Thread对象数据属性

name

线程名

ident

线程的标识符

daemon

布尔标志,表示这个线程是否为守护进程

Thread对象方法

start()

开始执行该线程

run()

定义线程功能的方法

join(timeout=None)

直至启动的线程终止之前一直挂起;除非给出了timeout(秒),否则会一直阻塞

getName()

返回线程名

setName()

设定线程名

isDaemon

如果是守护进程就返回True;否则返回False

#!/usr/bin/env python

import threading
from time import sleep,ctime
loops = [4,2]

def loop(nloop,nsec):
    print('start loop',nloop,'at:',ctime())
    sleep(nsec)
    print('loop',nloop,'done at:',ctime())

def main():
    print('start at:',ctime())
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = threading.Thread(target=loop,args=(i,loops[i]))
        threads.append(t)

    for i in nloops:
        threads[i].start()

    for i in nloops:        #wait for all
        threads[i].join()   #threads to finish
    print('all done at:',ctime())

if __name__ == '__main__':
    main()


=================================
start at: Wed Oct 25 15:07:45 2017
start loop 0 at: Wed Oct 25 15:07:45 2017
start loop 1 at: Wed Oct 25 15:07:45 2017
loop 1 done at: Wed Oct 25 15:07:47 2017
loop 0 done at: Wed Oct 25 15:07:49 2017
all done at: Wed Oct 25 15:07:49 2017

8、锁Lock

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

balance = 0
lock = threading.Lock()

def run_thread(n):
    for i in range(100000):
        # 先要获取锁:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要释放锁:
            lock.release()

当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。

获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放。

锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

侦测OpenWhisk的Web操作

我之前写过关于OpenWhisk的Web操作的文章,阐述了它们到底是如何允许你向客户端发送状态码和HTTP头,

198100
来自专栏武军超python专栏

2018年8月25日多线程编程总结

PYTHON 本身也支持多任务处理,并且提供了如下的操作方式 多线程多任务处理机制   (比较常用) 多进程多任务处理机制   (不常用,大型项目开发或者系...

12940
来自专栏轻量级微服务

微服务下跨语言 RPC 实现

目前主流的 Java 开发框架 Spring Boot,为了更方便集成 gRPC,自己开发了 spring-boot-starter-grpc,仅需简单的几行配...

31730
来自专栏JetpropelledSnake

Python入门之Python引用模块和查找模块路径

#这篇文章主要介绍了Python引用模块和Python查找模块路径的相关资料,需要的朋友可以参考下 模块间相互独立相互引用是任何一种编程语言的基础能力。对于“模...

49390
来自专栏Linyb极客之路

工作流引擎之activiti任务监听器

任务监听器只能添加到流程定义中的用户任务中。 注意它必须定义在BPMN 2.0 extensionElements的子元素中, 并使用activiti命名空间,...

41120
来自专栏木木玲

设计模式 ——— 职责链模式

16330
来自专栏别先生

Hadoop Shell命令(基于linux操作系统上传下载文件到hdfs文件系统基本命令学习)

Apache-->hadoop的官网文档命令学习:http://hadoop.apache.org/docs/r1.0.4/cn/hdfs_shell.html...

33970

检测OpenWhisk Web Actions

我已经写了一篇文章,它是关于OpenWhisk Web actions,以及他们是如何让你通过向客户端发送一个状态码和HTTP头后,在main()方法中得到一个...

19250
来自专栏Java后端技术

深入聊聊Java多线程

  在没有学习Java多线程以前,总觉得多线程是个很神秘的东西,只有那些大神才能驾驭,新年假期没事就来学习和了解一下Java的多线程,本篇博客我们就来从头说一下...

13040
来自专栏CaiRui

多线程编程

1、多线程对于具有如下特点的编程任务是非常理想的:1、本质上是异步的 2、需要多个并发活动 3、每个活动的处理顺序是不确定的。 2、使用多线程编程,以及类似Qu...

22490

扫码关注云+社区

领取腾讯云代金券