Python笔记_第四篇_高阶编程_进程、线程、协程_2.线程
1. 线程概述:
在一个进程的内部,要同时干多件事情,就需要同时运行“多个子任务”,我们把进程内的这些“子任务”叫做线程。也就说线程是进程成的子任务。
线程通常叫做情景的进程。线程是通过向内侧控件的并发执行的多任务。每一个线程都共享一个进程的资源。
线程是最小的执行单元,而进程至少由一个线程组成。如何调度进程和线程,完全由操作系统决定,用户程序不能自己决定什么时候执行。执行多长时间。
线程有两个模块:
_thread模块:低级模块(现在很少用了)
threading模块:高级模块,是对_thread的进一步封装。
2. 启动一个线程:
先上代码:
import threading,time a =
def run(num):
print("子线程(%s)开始" % threading.current_thread().name) # 实现线程的功能
print("打印",num)
time.sleep()
print(a) print("子线程(%s)结束" % threading.current_thread().name) if __name__ == '__main__':
# 任何进程默认就会启动一个线程,成为主线程,主线程可以启动新的子程序
# current_thread().name 返回当前线程的实例的名称
print("主程序(%s)启动" % (threading.current_thread().name)) # 创建子进程
t = threading.Thread(target=run,name="runThread",args=(,))
t.start()
# 等待线程结束
t.join() print("主程序(%s)结束" % (threading.current_thread().name)) 主程序(MainThread)启动
子线程(runThread)开始
打印 子线程(runThread)结束
主程序(MainThread)结束
说明1:任何进程默认就会启动一个线程,成为主线程,主线程可以启动新的子程序。
说明2:current_thread().name方法是返回一个档期线程的实例名称。
3. 线程间的数据共享存在的问题:
多线程和多进程最大的不同在于:多进程中,同一个变量,格子有一份拷贝存在在每个进程中,互不影响,而多线性中,所有变量都由所有线程共享,所以任何一个变量都可以被任意一个线程修改,因此线程间的通向数据存在的最大危险在于多个线程同时修改一个变量,容易把内存内容改乱了。
实例1:我们以加一个减一个的方式不停循环一个很大的数,我们发现,数越大到最后就出现了混乱,而不是还是等于0的状态。
import threading num = def run(n):
global num
for i in range():
num = num + n
num = num - n if __name__ == '__main__':
t1 = threading.Thread(target=run,args=(,))
t2 = threading.Thread(target=run,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() print("num = ", str(num))
# num = -
说明1:这种问题的存在,与进程不一样,而且为了达到相互的交替执行,而不出现随机性的混乱情况。我们加入了“锁”的概念。
4. 线程锁解决数据混乱:
在两个线程同时工作的时候,我们为了防止数据可能出现的混乱,加入了线程锁。
先上代码:
import threading # 创建锁对象
lock = threading.Lock() num = def run(n):
global num
for i in range():
# 锁
# 确保了这段代码由一个线程从头到尾的完成执行
# 阻止了多线程的并发执行,包含锁的某段代码,实际上只能以单线程模式执行,所以效率大大的降低了。
# 由于可以存在多个锁,不同线程可以持有不同的锁,并试图获取其他的锁,这样锁来锁去的,可能造成死锁,导致多个线程挂起。只能靠操作系统强制终止。
lock.acquire()
try:
num = num + n
num = num - n
finally:
# 开锁
lock.release() if __name__ == '__main__':
t1 = threading.Thread(target=run,args=(,))
t2 = threading.Thread(target=run,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() print("num = ", str(num)) # num =
# 上锁后成功。
说明:上锁我们用的方法是:lock.acquire()需要一把锁,解锁我们用的方法是:lock.release()释放这把锁。
说明2:这样确保了这段代码由一个线程从头到尾的完成执行。阻止了多线程并发(前面提到过)的执行,包含锁的代码,实际上只能单线程执行,所以效率大大降低了。
说明3:由于可以存在多个锁,不能线程可以持有不同的锁,并试图获取其他的锁,这样锁来锁去,可能造成死锁,导致多个线程挂起。只能靠系统强制终止。
说明4:因此我们采用try...finally异常的代码处理。这样写稍微麻烦一些,之前我们在学文件的io操作的时候,用过一个with的方式,用这种方式文件也不需要再关闭。因此我们用另外一种写法,让锁也不用关闭了,他会自动去关闭。
代码:
# 锁的第二种写法:
import threading # 创建锁对象
lock = threading.Lock() num = def run(n):
global num
for i in range():
# 与上面代码功能相同,with lock可以自动上锁与解锁。
with lock:
num = num + n
num = num - n if __name__ == '__main__':
t1 = threading.Thread(target=run,args=(,))
t2 = threading.Thread(target=run,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() print("num = ", str(num)) # num =
# 上锁后成功。
5. threadLocal:
我们发现通过上锁开锁的这样方式容易造成程序错误导致死机的情况。我们可以采取用本地分配线程变量的方式,让这些变量的计算有自己的独立运行线程也可以实现这样的功能。因此我们把下面代码中的num变量交送给“local”的线程变量,让local在每个线程中独立去交互数据。
代码:
import threading num =
# 创建一个全局的ThreadLocal对象
# 每个线程有的独立存储空间
# 每个线程对ThreadLocal对象,而且互不影响。
local = threading.local() def run(x,n):
x = x + n
x = x - n def func(n):
# 每个线程都有local.x,就是线程的局部变量
local.x = num
for i in range():
run(local.x,n)
print("%s--%d" %(threading.current_thread().name,local.x)) if __name__ == '__main__':
t1 = threading.Thread(target=func,args=(,))
t2 = threading.Thread(target=func,args=(,))
t1.start()
t2.start()
t1.join()
t1.join() # Thread - - -
# Thread - - -
# 所用:为每个线程绑定一个数据库连接,或者是HTTP请求,或者是用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便的访问这些资源。
6. 信号量控制线程数量(Semaphore):
前面我们发现了,我们手工的去制作一个线程,但是我们其实不知道当前有多少个线程,这些线程的多少我们可以叫做信号量,我们可以通过信号量的多少来控制线程。
代码:
import threading,time # 控制线程的数量,让多少个线程一起执行
sem = threading.Semaphore() def run():
with sem:
for i in range():
print("%s - %d" %(threading.current_thread().name,i))
time.sleep()
if __name__ == '__main__':
for i in range():
threading.Thread(target=run).start() Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- - 2Thread- - Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- -
Thread- - 0Thread- - Thread- -
Thread- -
Thread- - 2Thread- - Thread- -
Thread- -
Thread- - 4Thread- -
说明1:控制线程的数量,让多少个线程一起执行。
7. 凑够一定数量才能一起执行(Barrier):
代码:
import threading,time # 凑够多少个线程一起执
bar = threading.Barrier() def run():
print("%s - start" %(threading.current_thread().name))
time.sleep()
bar.wait()
print("%s - end" % (threading.current_thread().name))
# 如果凑不够一定数量不会执行,一直在那里等着 if __name__ == '__main__':
for i in range():
threading.Thread(target=run).start()
说明1:我们发现程序一直停在那儿等待凑够一定的线程数量才能一起执行。其实这种方式在多数去情况下是用不到的。
8. 定时线程(Timer):
我们之前又给线程上锁,又分配线程量的情况,我们如果想给线程定时去开启,需要用到Timer的方法。
代码:
import threading def run():
print("Thomas is a good man") # 延时执行线程
t = threading.Timer(,run) t.start()
t.join() print("父线程结束")
# Thomas is a good man
# 父线程结束
说明1:这样我们等待了5秒钟,线程才开始执行程序。
9. 线程通信(event):
前面我们使用手工的方式进行进行线程之间上锁解锁之间的方式进行性通讯的。我们可以调用线程的事件(因为线程行动本身就是一个事件),让上一个线程等待时间触发。
代码:
import threading,time def func():
# 事件对象
event = threading.Event()
def run():
for i in range():
# 阻塞,等待时间的触发
event.wait()
# clear是重置的意思
event.clear()
print("Thomas is a good man!!%d",i) threading.Thread(target=run).start()
return event e = func()
# 触发时间
for i in range():
e.set()
time.sleep()
10. 线程间的通信(Queue队列方式):
代码:
import threading,queue,time,random # 生产者
def producer(id,q):
while True:
num = random.randint(,)
q.put(num)
print("生产者%d生产了%d的数据放入了队列" %(id,num))
time.sleep()
# 任务完成
q.task_done() # 消费者
def customer(id,q):
while True:
item = q.get()
if item is None:
break
print("消费者%d消费了%d数据" %(id,item))
time.sleep()
# 任务完成
q.task_done() if __name__ == '__main__':
# 消息队列
q = queue.Queue() # 启动生产者
for i in range():
threading.Thread(target=producer,args=(i,q)).start() # 启动消费者
for i in range():
threading.Thread(target=customer,args=(i,q)).start()
11. 线程的调度(Condition方法):
前面用事件去控制线程的开关,我们还可以通过线程的条件来控制。
代码:
import threading,time # 线程条件变量
cond = threading.Condition() def run1():
with cond:
for i in range(,,):
print(threading.current_thread().name,i)
time.sleep()
cond.wait()
cond.notify() def run2():
with cond:
for i in range(,,):
print(threading.current_thread().name,i)
time.sleep()
cond.notify()
cond.wait() threading.Thread(target=run1).start()
threading.Thread(target=run2).start()
说明:这里有一点注意的是cond.wait控制等待,然后要通知下一个cond.notify这样才可以。
Python笔记_第四篇_高阶编程_进程、线程、协程_2.线程的更多相关文章
- Python笔记_第四篇_高阶编程_进程、线程、协程_4.协程
1.协程的概念: 子程序或者子函数,在所有语言中都是层级调用,比如A调用B,再B执行的过程中又可以调用C,C执行完毕返回,B执行返回,最后是A执行完毕返回.是通过栈来实现的,一个线程就是执行一个自称, ...
- Python笔记_第四篇_高阶编程_进程、线程、协程_5.GPU加速
Numba:高性能计算的高生产率 在这篇文章中,笔者将向你介绍一个来自Anaconda的Python编译器Numba,它可以在CUDA-capable GPU或多核cpu上编译Python代码.Pyt ...
- Python笔记_第四篇_高阶编程_进程、线程、协程_1.进程
1. 多任务原理: 现代操作系统,像win,max os x,linux,unix等都支持多任务. * 什么叫做多任务? 操作系统可以同时运行多个任务. * 单核CPU实现多任务原理? 操作系统轮流让 ...
- Python开发【第十三篇】高阶函数、递归函数、闭包
函数式编程是指用一系列函数解决问题 好处:用每个函数完成每个细小的功能,一系列函数任意组合能够解决大问题 函数仅仅接收输入并产生输出,不包含任何能影响输出的内部状态 函数之间的可重入性 当一个函数的输 ...
- python学习三十四天函数高阶函数定义及用法
python函数高阶函数是把函数当成一个变量,传递给函数作为参数,或者函数的返回值里面有函数,都称为高阶函数, 1,把函数作为参数传递 def dac(x,y): return x+y def tes ...
- 多任务-python实现-进程,协程,线程总结(2.1.16)
@ 目录 1.类比 2.总结 关于作者 1.类比 一个生产玩具的工厂: 一个生产线成为一个进程,一个生产线有多个工人,所以工人为线程 单进程-多线程:一条生产线,多个工人 多进程-多线程:多条生产线, ...
- Python笔记_第四篇_高阶编程_实例化方法、静态方法、类方法和属性方法概念的解析。
1.先叙述静态方法: 我们知道Python调用类的方法的时候都要进行一个实例化的处理.在面向对象中,一把存在静态类,静态方法,动态类.动态方法等乱七八糟的这么一些叫法.其实这些东西看起来抽象,但是很好 ...
- Python笔记_第四篇_高阶编程_魔法(术)方法详解(重载的再详解)
1. 魔法方法是什么? 魔法方法(Magic Method)是Python比较独特的应用,它可以给你的类增加特殊的方法,如果你的对象实现了(重载),这些方法中的某一个,就会被Python所调用.正如装 ...
- Python笔记_第四篇_高阶编程_再议装饰器和再议内置函数
1. 概述: 我们在前面用了很多的装饰器这个工具的方法.这个位置要系统的讲一下装饰器. 1.2 为什么需要装饰器. 装饰器本质是一个Python函数,它可以让其他函数在不需要任何代码变动的前提下增加额 ...
- Python笔记_第四篇_高阶编程_正则表达式_2.正则表达式入门
1. 匹配单个字符和数字: . --->> 匹配除换行符以外的任意字符.[0123456789] --->> []字符集合,表示匹配方括号中所包含的任意一个字符.[Thomas ...
随机推荐
- mybatis关于级联查询结果集嵌套映射对象非列表的处理问题
工作中遇到这么一个问题,嵌套查询,返回json的时候,作为属性,deviceFields是一个device中的一个对象属性,在json返回的时候想要得到的应该是deviceFields:{ 具体属性} ...
- Redis 详解 (五) redis的五大数据类型实现原理
目录 1.对象的类型与编码 ①.type属性 ②.encoding 属性和 *prt 指针 2.字符串对象 3.列表对象 4.哈希对象 5.集合对象 6.有序集合对象 7.五大数据类型的应用场景 8. ...
- 【java】【反射】反射实现判断发生了修改操作,判断两个对象是否发生属性值的变更,判断两个List集合内对象的属性值是否发生变更
java的反射实现: 判断发生了修改操作,判断两个对象是否发生属性值的变更,判断两个List集合内对象的属性值是否发生变更 今日份代码: package com.sxd.streamTest; imp ...
- 本地Redis服务配置
本地Redis服务配置 要求:在虚拟机中启动redis服务,并要在windows物理机上取得链接 虚拟机安装略,(结果如下) windows工作机上装了Oracle VM VirtualBox,并在其 ...
- Flink Task 并行度
并行的数据流 Flink程序由多个任务(转换/运算符,数据源和接收器)组成,Flink中的程序本质上是并行和分布式的. 在执行期间,流具有一个或多个流分区,并且每个operator具有一个或多个ope ...
- python 同步异步,并发并行,同步锁
并发:系统具有处理多个任务(动作)的能力 并行:系统具有同时处理多个任务(动作)的能力 同步:当进程执行到一个IO(等待外部数据)的时候,需要等待,等待即同步 异步:当进程执行到一个IO(等待外部数据 ...
- MFC 选择文件夹
WCHAR szPath[_MAX_PATH] = {}; BROWSEINFO bi; //指定父窗口,在对话框显示期间,父窗口将被禁用 bi.hwndOwner = this->GetSaf ...
- C++的模板类:不能将定义与声明写在不同文件中
问题来源 今天看了orbslam2自带的第三方库DBoW2的TemplatedVocabulary.h文件,发现其中模板类的函数成员的定义与声明放在了同一个文件:同时发现,DBoW2的CMakeLis ...
- 五、SAP中定义变量和给变量赋值
一.代码如下: 二.执行效果图,如下:
- 利用方法HttpUtility.HtmlEncode来预处理用户输入
利用方法HttpUtility.HtmlEncode来预处理用户输入.这样能阻止用户用链接注入JavaScript代码或HTML标记,比如//Store/Broswe?Genre=<script ...