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 ...
随机推荐
- 定义一个数,它可能为正 也可能为负 var num = Math.pow(-1,parseInt(Math.random() * 2) + 1);
// 定义一个随机数范围从0 ~页面宽度 var x = parseInt(Math.random() * myCanvas.width); // 定义一个随机数 范围从0 ~页面高度 var y = ...
- 如何转换MySQL表的引擎
有很多种方法可以将表的存储引擎转换成另一种引擎.每种方法都有其优缺点,在这里介绍四种方法: 选择优先级(pt-online-schema-change > 创建与查询 > 导出和导入 &g ...
- windbg指定SOS版本,执行扩展命令报错
调试dump文件,加载相匹配版本的sos/clr时,绝大多数都是可以正常使用的. 然而凡事都有例外,今天在做类似工作时,遇到了错误: CLRDLL: Consider using ".cor ...
- MongoDB批量操作及与MySQL效率对比
本文主要通过批量与非批量对比操作的方式介绍MongoDB的bulkWrite()方法的使用.顺带与关系型数据库MySQL进行对比,比较这两种不同类型数据库的效率.如果只是想学习bulkWrite()的 ...
- RPC-非阻塞通信下的同步API实现原理,以Dubbo为例
Netty在Java NIO领域基本算是独占鳌头,涉及到高性能网络通信,基本都会以Netty为底层通信框架,Dubbo 也不例外.以下将以Dubbo实现为例介绍其是如何在NIO非阻塞通信基础上实现同步 ...
- 我使用 Docker 部署 Celery 遇到的问题
问题1 - Sending due task 本机测试时没有问题的,但是在线上 docker 中,任务一直显示 "Sending due task".超时的任务是 Django O ...
- 【JavaScript声明变量的规则】
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- [bzoj4240] 有趣的家庭菜园
还是膜网上题解QAQ 从低到高考虑,这样就不会影响后挪的草了. 每次把草贪心地挪到代价较小的一边.位置为i的草,花费为min( 1..i-1中更高的草的数目,i+1..n中更高的草的数目 ) 因为更小 ...
- maven学习3,如何创建一个web项目
Maven学习 (三) 使用m2eclipse创建web项目 1.首先确认你的eclipse已经安装好m2eclipse的环境,可以参照上两篇Maven学习内容 2.新建一个maven的项目 ...
- struts中用kindeditor实现的图片上传并且显示在页面上
做公司网站的时候由于需要在内容属性中加入图片,所以就有了这个问题,本来一开始找几篇文章看都是讲修改kindeditor/jsp/file_manager_json.jsp和upload_json.js ...