python --- 基础多线程编程
在python中进行多线程编程之前必须了解的问题:
1. 什么是线程?
答:线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。
2. 什么是多线程?
答:在单个程序中同时运行多个线程完成不同的工作,称为多线程。
3. 多线程编程的目的?
答:多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。
4. 如何再python中执行多线程编程?
答:在python2.x的版本中提供了thread(这个模块为多线程提供了一个底层 、原始的操作[也可以成为light-weight processes 或者 tasks) — 多个控制线程共享全局数据空间。为了多线程同步,提供了简单的锁(也称呼为 mutexes 后者 binary semaphores) 。]和threading(本模块的高层线程接口构建在低层的thread模块上)两个模块用于线程操作;而在python3.x中,官方只给出了threading模块的文档,对于底层线程造作放在了_thread模块中(即不建议使用)。是故在python中使用threading模块编程即可。
例一(一个简单的双线程程序):
import threading
import time def run(n):
print("task-%s" % n)
time.sleep(5) #实例化一个线程对象,target传入任务名,args以元组的形式传入任务函数的参数
task1 = threading.Thread(target=run, args=(1,))
task2 = threading.Thread(target=run, args=(2,)) task1.start() #线程启动
task2.start()
test_threads_1
注:执行上面这个程序一共花费5秒左右的时间,而如果分别调用两次run怎需要10秒(证明两个任务同时运行)。
例二(用面向对象编程实现例二,并讲解threading模块中的一些常用方法):
import threading
import time class Task(threading.Thread):
def __init__(self, n):
super(Task, self).__init__() #重构了__init__(), 所以先执行父类的构造函数
self.n = n #构造函数中传入任务参数参数 #任务函数
def run(self):
"""
不同于例一,在类中,任务函数只能以run命名,参数从构造函数中传入
:return:
"""
print("task-%s" % self.n)
# print(threading.current_thread()) #threading.current_thread()返回当前的Thread对象,对应于调用者控制的线程。
time.sleep(2) #实例化并启动
t1 = Task(1)
t2 = Task(2)
# t1.setDaemon(True) #将t1设置成为守护线程
# t2.setDaemon(True) t1.start()
#t1.join(timeout=5) #等待t1结束再运行t2,timeout代表等待时间,超过5秒便不再等待。
t2.start() # print(threading.current_thread())
# print(threading.active_count()) #threading.active_count()返回当前处于alive状态的Thread对象的个数。
test_threads_2
注:建议使用例二这种编程方式,在类中,任务函数只能以run命名,参数从构造函数中传入。
注:有时候难免会遇到一个问题,主程序的执行需要某个子线程(即使用start启动的线程,默认子线程一旦启动主程序将继续运行,两者不再有关联)的执行结果,这时使用join即可,例如例二中的t1.join()。
守护线程:一种特殊的子线程,与主线程(即本文中的主程序,由程序使用者启动)存在一种“主死仆死”的关系,即主线程意外停止或运行结束,不管守护线程是否运行完毕都得终止(非守护线程的子线程启动后与主线程没有联系,不论主线程是否还在运行,子线程不受影响),一个线程是否为守护线程继承于创建它的线程(将一个子线程设置成为守护线程可参考例二,去掉22,23行注释,则主程序结束t1,t2也结束,不会再sleep)。
在进行多线程编程时,经常会有多个线程同时对同一份数据修改,这便会导致最终得得到的数据不是预期结果。所以需要在一个线程修改数据时将数据锁定,只允许当前线程修改,当修改完成后,解锁让其他线程修改。在threading模块中使用Lock Objects解决此问题。
Lock Objects:
A primitive lock is a synchronization primitive that is not owned by a particular thread when locked. In Python, it is currently the lowest level synchronization primitive available, implemented directly by the _thread
extension module.
A primitive lock is in one of two states, “locked” or “unlocked”. It is created in the unlocked state. It has two basic methods, acquire()
and release()
. When the state is unlocked, acquire()
changes the state to locked and returns immediately. When the state is locked, acquire()
blocks until a call to release()
in another thread changes it to unlocked, then the acquire()
call resets it to locked and returns. The release()
method should only be called in the locked state; it changes the state to unlocked and returns immediately. If an attempt is made to release an unlocked lock, a RuntimeError
will be raised.
Locks also support the context management protocol.
When more than one thread is blocked in acquire()
waiting for the state to turn to unlocked, only one thread proceeds when a release()
call resets the state to unlocked; which one of the waiting threads proceeds is not defined, and may vary across implementations.
All methods are executed atomically.
例三(多个线程对一个全局变量进行加1操作):
import threading,time n = 5 class Task(threading.Thread):
def __init__(self, n):
super(Task, self).__init__()
self.name = "Thread-" + str(n) def run(self):
"""
不同于例一,在类中,任务函数只能以run命名,参数从构造函数中传入
:return:
"""
global n
time.sleep(0.5)
#锁定资源, 此时关于n的操作只能此线程执行
if lock.acquire():
n += 1
print("%s, n = %s" % (self.name, n))
time.sleep(0.2)
lock.release() #解锁 if __name__ == "__main__": lock = threading.Lock() #实例化一个锁,此时锁处于打开状态 ts = [] #用于存储线程对象
#循环启动50个线程
for i in range(1, 50+1):
t = Task(i)
ts.append(t)
t.start()
#等待所有线程结束
start_time = time.time()
for t in ts:
t.join() print("run time:",time.time() - start_time)
test_threads_3
注:运行此程序,可发现一共用时10.5(0.2*50 + 0.5)秒左右,可以发现在锁定代码段程序没有并行执行。
注:有时候需要多层加锁,这时Lock Objects已经满足不了这个需求(当Lock处于locked时,遇到下一个acquire()时会阻塞)。这时我们使用RLock Objects,它的调用同Lock Objects。
信号量:信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。
举例说明:以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
import threading
import time class Task(threading.Thread):
#任务函数
def __init__(self, n):
super(Task, self).__init__()
self.name = "Thread-" + str(n) def run(self):
semaphore.acquire()
print("%s" % self.name)
time.sleep(2)
semaphore.release() if __name__ == "__main__": semaphore = threading.BoundedSemaphore(5) #只允许5个线程同时访问资源 ts = [] #用于存储线程对象
#循环启动50个线程 for i in range(1, 23+1):
t = Task(i)
ts.append(t)
t.start()
test_threads_4
An event object manages an internal flag that can be set to true with the set()
method and reset to false with the clear()
method. The wait()
method blocks until the flag is true(每个事件对象管理一个内部标志,可以在事件对象上调用set()
方法将内部标志设为true,调用 clear()
方法将内部标志重置为false。wait()
方法将阻塞直至该标志为真。).
例五(简单的线程交互程序,演示Event Objects的使用):
import threading,time class Task1(threading.Thread):
def run(self):
while True:
for i in range(1, 20+1):
print("i = ", i)
if i % 5 == 0:
eve.set() #到5的倍数,将Event内部标志设置为True
time.sleep(0.5) class Task2(threading.Thread):
def run(self):
while True:
#检测内部标志是否为True
if eve.is_set():
print("hello world")
time.sleep(2)
eve.clear() #重置Event内部标志
else:
eve.wait() #内部标志不为True,此线程处于阻塞状态 if __name__ == "__main__":
t1 = Task1()
t2 = Task2()
eve = threading.Event() #实例化一个Event Objects t1.start()
t2.start()
test_threads_5
python --- 基础多线程编程的更多相关文章
- python基础-函数式编程
python基础-函数式编程 高阶函数:map , reduce ,filter,sorted 匿名函数: lambda 1.1函数式编程 面向过程编程:我们通过把大段代码拆成函数,通过一层一层 ...
- python基础——面向对象编程
python基础——面向对象编程 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的 ...
- python 基础网络编程2
python 基础网络编程2 前一篇讲了socketserver.py中BaseServer类, 下面介绍下TCPServer和UDPServer class TCPServer(BaseServer ...
- python 基础网络编程1
python 基础网络编程1 Source code: Lib/socketserver.py lib的主目录下有一个sockserver.py文件, 里面是python基本的网络编程模型 共有一个b ...
- Python的多线程编程
提到多线程,很多人就会望而却步,本文将由浅入深地带你攻克python多线程编程,并防止你跳入深坑, 首先看一段简单的代码: from time import ctime,sleep def play_ ...
- python基础网络编程--转
python之网络编程 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的 ...
- 【转】使用python进行多线程编程
1. python对多线程的支持 1)虚拟机层面 Python虚拟机使用GIL(Global Interpreter Lock,全局解释器锁)来互斥线程对共享资源的访问,暂时无法利用多处理器的优势.使 ...
- Python基础-socket编程
一.网络编程 自从互联网诞生以来,现在基本上所有的程序都是网络程序,很少有单机版的程序了. 计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信.网络编程就是如何在程序中实现两台计算机的 ...
- python基础和编程库
Python编程从入门到实践-------基础入门 1.Python中的变量 2.Python首字母大写使用title()方法,全部大写upper()方法,全部小写lower()方法 3.Python ...
随机推荐
- Django-环境搭建
django开发环境搭建 环境说明 python django mysql pymysql pycharm 安装过程 安装python3.6.3 64位下载地址: https://www.python ...
- PHP开发工程师应该具备那些技术能力
根据各大招聘网站的数据分析,2017年IT行业,其中从事软件开发和数据分析的毕业生的月薪最高.究其原因是因为随着互联网的不断发展,岗位人才稀缺,需求量大.那么如何快速的成为软件开发人员,比如PHP开发 ...
- FileSaver.js 介绍
这是著名开源项目 FileSaver.js 的 README.md,我把它翻译成中文.发出来,方便自己和他人阅读. 项目地址:https://github.com/eligrey/FileSaver. ...
- JavaScript判断对象类型及节点类型、节点名称和节点值
一.JavaScript判断对象类型 1.可以使用typeof函数判断对象类型 function checkObject1(){ var str="str"; console.lo ...
- JavaScript中常用的正则表达式日常整理(全)
//校验是否全由数字组成 ? 1 2 3 4 5 6 function isDigit(s) { var patrn=/^[0-9]{1,20}$/; if (!patrn.exec(s)) retu ...
- HDU 1166 敌兵布阵(线段树单点更新,板子题)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- [bzoj2836] 魔法树
俩操作:增加路径上的点的权值.查询子树的权值和. 想了想似乎只能树链剖分了..好久没写链剖+数据结构了TAT 一开始没开LL炸了一发(明明有想到的..我果然是傻逼= = #include<cst ...
- flume1.8 Sinks类型介绍(三)
1. Flume Sinks 1.1 HDFS Sink 该sink把events写进Hadoop分布式文件系统(HDFS).它目前支持创建文本和序列文件.它支持在两种文件类型压缩.文件可以基于数据的 ...
- sizeof与strlen的不同
sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型. 该类型保证能容纳实现所建立的最大对象的字节大小. sizeof是算符,strlen是函数. si ...
- UE4 分层材质 Layerd Materials
在UE4中最正规的材质制作流程就像: 建立新材质,并将其调整为达至完美. 在内容浏览器中,建立新材质函数,并将所有材质函数节点复制/粘贴到其中. 将网络连接到新的 Make Material Attr ...