原文:https://www.cnblogs.com/chengd/articles/7770898.html

https://blog.csdn.net/sunhuaqiang1/article/details/70168015

https://yq.aliyun.com/articles/26041?spm=5176.10695662.1996646101.searchclickresult.5d947f68X55AxH------[python] 专题八.多线程编程之thread和threading

https://yq.aliyun.com/articles/621687?spm=5176.10695662.1996646101.searchclickresult.5d947f68X55AxH---Python多线程threading进阶笔记

https://yq.aliyun.com/articles/558679?spm=5176.10695662.1996646101.searchclickresult.5d947f68X55AxH---threading多线程总结

https://www.u3v3.com/ar/1383-----------python3.6 - threading 多线程编程进阶,线程间并发控制(2)

daemon进程,理解为后台进程。

只要主进程没看到有daemon为None或daemon=False进程,就会把daemon进程杀掉。

if daemon is not None:
self._daemonic = daemon
else:
self._daemonic = current_thread().daemon

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)

thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。

下面将介绍threading模块常用方法:

1. threading.Lock()

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。

来看看多个线程同时操作一个变量怎么把内容给改乱了(在windows下不会出现内容混乱情况,可能python在Windows下自动加上锁了;不过在Linux 下可以测试出内容会被改乱):

#!/usr/bin/env python3

import time, threading

# 假定这是你的银行存款:
balance = 0 def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n def run_thread(n):
for i in range(100000):
change_it(n) t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
执行结果:
[root@localhost ~]# python3 thread_lock.py
5
[root@localhost ~]# python3 thread_lock.py
5
[root@localhost ~]# python3 thread_lock.py
0
[root@localhost ~]# python3 thread_lock.py
8
[root@localhost ~]# python3 thread_lock.py
-8
[root@localhost ~]# python3 thread_lock.py
5
[root@localhost ~]# python3 thread_lock.py
-8
[root@localhost ~]# python3 thread_lock.py
3
[root@localhost ~]# python3 thread_lock.py
5

我们定义了一个共享变量balance,初始值为0,并且启动两个线程,先存后取,理论上结果应该为0,但是,由于线程的调度是由操作系统决定的,当t1、t2交替执行时,只要循环次数足够多,balance的结果就不一定是0了。

如果我们要确保balance计算正确,就要给change_it()上一把锁,当某个线程开始执行change_it()时,我们说,该线程因为获得了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:

#!/usr/bin/env python3

import time, threading

balance = 0
lock = threading.Lock() def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
执行结果:
[root@localhost ~]# python3 thread_lock.py
0
[root@localhost ~]# python3 thread_lock.py
0
[root@localhost ~]# python3 thread_lock.py
0
[root@localhost ~]# python3 thread_lock.py
0
[root@localhost ~]# python3 thread_lock.py
0
[root@localhost ~]# python3 thread_lock.py
0

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

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

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

2. threading.Rlock()

RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

import threading
lock = threading.Lock()
#Lock对象
lock.acquire()
lock.acquire()
#产生了死琐。
lock.release()
lock.release() import threading
rLock = threading.RLock()
#RLock对象
rLock.acquire()
rLock.acquire()
#在同一线程内,程序不会堵塞。
rLock.release()
rLock.release()

3. threading.Condition()

可以把Condiftion理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。threadiong.Condition在内部维护一个琐对象(默认是RLock),可以在创建Condigtion对象的时候把琐对象作为参数传入。Condition也提供了acquire, release方法,其含义与琐的acquire, release方法一致,其实它只是简单的调用内部琐对象的对应的方法而已。Condition还提供wait方法、notify方法、notifyAll方法(特别要注意:这些方法只有在占用琐(acquire)之后才能调用,否则将会报RuntimeError异常。):

acquire()/release():获得/释放 Lock

    wait([timeout]):线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。调用wait()会释放Lock,直至该线程被Notify()、NotifyAll()或者超时线程又重新获得Lock.

    notify(n=1):通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock。

    notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程(这个一般用得少)

现在写个捉迷藏的游戏来具体介绍threading.Condition的基本使用。假设这个游戏由两个人来玩,一个藏(Hider),一个找(Seeker)。游戏的规则如下:1. 游戏开始之后,Seeker先把自己眼睛蒙上,蒙上眼睛后,就通知Hider;2. Hider接收通知后开始找地方将自己藏起来,藏好之后,再通知Seeker可以找了; 3. Seeker接收到通知之后,就开始找Hider。Hider和Seeker都是独立的个体,在程序中用两个独立的线程来表示,在游戏过程中,两者之间的行为有一定的时序关系,我们通过Condition来控制这种时序关系。

#!/usr/bin/python3.4
# -*- coding: utf-8 -*- import threading, time def Seeker(cond, name):
time.sleep(2)
cond.acquire()
print('%s :我已经把眼睛蒙上了!'% name)
cond.notify()
cond.wait()
for i in range(3):
print('%s is finding!!!'% name)
time.sleep(2)
cond.notify()
cond.release()
print('%s :我赢了!'% name) def Hider(cond, name):
cond.acquire()
cond.wait()
for i in range(2):
print('%s is hiding!!!'% name)
time.sleep(3)
print('%s :我已经藏好了,你快来找我吧!'% name)
cond.notify()
cond.wait()
cond.release()
print('%s :被你找到了,唉~^~!'% name) if __name__ == '__main__':
cond = threading.Condition()
seeker = threading.Thread(target=Seeker, args=(cond, 'seeker'))
hider = threading.Thread(target=Hider, args=(cond, 'hider'))
seeker.start()
hider.start()
执行结果:
seeker :我已经把眼睛蒙上了!
hider is hiding!!!
hider is hiding!!!
hider :我已经藏好了,你快来找我吧!
seeker is finding!!!
seeker is finding!!!
seeker is finding!!!
seeker :我赢了!
hider :被你找到了,唉~^~!

上面不同颜色的notify和wait一一对应关系,通知--->等待;等待--->通知

4. threading.Semaphore和BoundedSemaphore

Semaphore:Semaphore 在内部管理着一个计数器。调用 acquire() 会使这个计数器 -1,release() 则是+1(可以多次release(),所以计数器的值理论上可以无限).计数器的值永远不会小于 0,当计数器到 0 时,再调用 acquire() 就会阻塞,直到其他线程来调用release()

import threading, time

def run(n):
# 获得信号量,信号量减一
semaphore.acquire()
time.sleep(1)
print("run the thread: %s" % n) # 释放信号量,信号量加一
semaphore.release()
#semaphore.release() # 可以多次释放信号量,每次释放计数器+1
#semaphore.release() # 可以多次释放信号量,每次释放计数器+1 if __name__ == '__main__': num = 0
semaphore = threading.Semaphore(2) # 最多允许2个线程同时运行(即计数器值);在多次释放信号量后,计数器值增加后每次可以运行的线程数也会增加
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start() while threading.active_count() != 1:
pass # print threading.active_count()
else:
print('----all threads done---')
print(num)

BoundedSemaphore:类似于Semaphore;不同在于BoundedSemaphore 会检查内部计数器的值,并保证它不会大于初始值,如果超了,就引发一个 ValueError。多数情况下,semaphore 用于守护限制访问(但不限于 1)的资源,如果 semaphore 被 release() 过多次,这意味着存在 bug

import threading, time

def run(n):
semaphore.acquire()
time.sleep(1)
print("run the thread: %s" % n)
semaphore.release()
# 如果再次释放信号量,信号量加一,这是超过限定的信号量数目,这时会报错ValueError: Semaphore released too many times
#semaphore.release() if __name__ == '__main__': num = 0
semaphore = threading.BoundedSemaphore(2) # 最多允许2个线程同时运行
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start() while threading.active_count() != 1:
pass # print threading.active_count()
else:
print('----all threads done---')
print(num)

5. threading.Event

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞;如果“Flag”值为True,那么执行event.wait 方法时便不再阻塞。

clear:将“Flag”设置为False
    set:将“Flag”设置为True
    用 threading.Event 实现线程间通信,使用threading.Event可以使一个线程等待其他线程的通知,我们把这个Event传递到线程对象中,

Event默认内置了一个标志,初始值为False。一旦该线程通过wait()方法进入等待状态,直到另一个线程调用该Event的set()方法将内置标志设置为True时,该Event会通知所有等待状态的线程恢复运行。

通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。

import threading, time
import random def light():
if not event.isSet(): #初始化evet的flag为真
event.set() #wait就不阻塞 #绿灯状态
count = 0
while True:
if count < 10:
print('\033[42;1m---green light on---\033[0m')
elif count < 13:
print('\033[43;1m---yellow light on---\033[0m')
elif count < 20:
if event.isSet():
event.clear()
print('\033[41;1m---red light on---\033[0m')
else:
count = 0
event.set() #打开绿灯
time.sleep(1)
count += 1 def car(n):
while 1:
time.sleep(random.randrange(3, 10))
#print(event.isSet())
if event.isSet():
print("car [%s] is running..." % n)
else:
print('car [%s] is waiting for the red light...' % n)
event.wait() #红灯状态下调用wait方法阻塞,汽车等待状态 if __name__ == '__main__':
car_list = ['BMW', 'AUDI', 'SANTANA']
event = threading.Event()
Light = threading.Thread(target=light)
Light.start()
for i in car_list:
t = threading.Thread(target=car, args=(i,))
t.start()

6. threading.active_count()

返回当前存活的线程对象的数量;通过计算len(threading.enumerate())长度而来

The returned count is equal to the length of the list returned by enumerate().

import threading, time

def run():
thread = threading.current_thread()
print('%s is running...'% thread.getName()) #返回线程名称
time.sleep(10) #休眠10S方便统计存活线程数量 if __name__ == '__main__':
#print('The current number of threads is: %s' % threading.active_count())
for i in range(10):
print('The current number of threads is: %s' % threading.active_count()) #返回当前存活线程数量
thread_alive = threading.Thread(target=run, name='Thread-***%s***' % i)
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName())

7. threading.current_thread()

Return the current Thread object, corresponding to the caller's thread of control.

返回当前线程对象

>>> threading.current_thread
<function current_thread at 0x00000000029F6C80>
>>> threading.current_thread()
<_MainThread(MainThread, started 4912)>
>>> type(threading.current_thread())
<class 'threading._MainThread'>

继承线程threading方法;通过help(threading.current_thread())查看。

import threading, time

def run(n):
thread = threading.current_thread()
thread.setName('Thread-***%s***' % n) #自定义线程名称
print('-'*30)
print("Pid is :%s" % thread.ident) # 返回线程pid
#print('ThreadName is :%s' % thread.name) # 返回线程名称
print('ThreadName is :%s'% thread.getName()) #返回线程名称
time.sleep(2) if __name__ == '__main__':
#print('The current number of threads is: %s' % threading.active_count())
for i in range(3):
#print('The current number of threads is: %s' % threading.active_count()) #返回当前存活线程数量
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName())
执行结果:
Pid is :11792
ThreadName is :Thread-***0***
------------------------------
Pid is :12124
ThreadName is :Thread-***1***
------------------------------
Pid is :11060
ThreadName is :Thread-***2*** MainThread thread is done...

8. threading.enumerate()

Return a list of all Thread objects currently alive

返回当前存在的所有线程对象的列表

import threading, time

def run(n):
thread = threading.current_thread()
thread.setName('Thread-***%s***' % n)
print('-'*30)
print("Pid is :%s" % thread.ident) # 返回线程pid
#print('ThreadName is :%s' % thread.name) # 返回线程名称
print('ThreadName is :%s'% threading.enumerate()) #返回所有线程对象列表
time.sleep(2) if __name__ == '__main__':
#print('The current number of threads is: %s' % threading.active_count())
threading.main_thread().setName('Chengd---python')
for i in range(3):
#print('The current number of threads is: %s' % threading.active_count()) #返回当前存活线程数量
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName())
执行结果:
Pid is :12096
ThreadName is :[<_MainThread(Chengd---python, started 12228)>, <Thread(Thread-***0***, started 12096)>, <Thread(Thread-2, initial)>]
------------------------------
Pid is :10328
ThreadName is :[<_MainThread(Chengd---python, started 12228)>, <Thread(Thread-***0***, started 12096)>, <Thread(Thread-***1***, started 10328)>, <Thread(Thread-3, initial)>]
------------------------------
Pid is :6032
ThreadName is :[<_MainThread(Chengd---python, started 12228)>, <Thread(Thread-***0***, started 12096)>, <Thread(Thread-***1***, started 10328)>, <Thread(Thread-***2***, started 6032)>] Chengd---python thread is done...

9.threading.get_ident()

返回线程pid

import threading, time

def run(n):
print('-'*30)
print("Pid is :%s" % threading.get_ident()) # 返回线程pid if __name__ == '__main__':
threading.main_thread().setName('Chengd---python') #自定义线程名
for i in range(3):
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName()) #获取线程名

10. threading.main_thread()

返回主线程对象,类似 threading.current_thread();只不过一个是返回当前线程对象,一个是返回主线程对象

import threading, time

def run(n):
print('-'*30)
print("Now Pid is :%s" % threading.current_thread().ident) # 返回当前线程pid
print("Main Pid is :%s" % threading.main_thread().ident) # 返回主线程pid
print('Now thread is %s...' % threading.current_thread().getName()) # 获取当前线程名
print('Main thread is %s...' % threading.main_thread().getName()) # 获取主线程线程名 if __name__ == '__main__':
threading.main_thread().setName('Chengd---python') #自定义线程名
for i in range(3):
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
time.sleep(2)
thread_alive.join()
执行结果:
------------------------------
Now Pid is :8984
Main Pid is :3992
Now thread is Thread-1...
Main thread is Chengd---python...
------------------------------
Now Pid is :4828
Main Pid is :3992
Now thread is Thread-2...
Main thread is Chengd---python...
------------------------------
Now Pid is :12080
Main Pid is :3992
Now thread is Thread-3...
Main thread is Chengd---python...

廖大线程讲解

线程常用模块方法

多线程之Condition()

python—threading.Semaphore和BoundedSemaphore

python 多线程之信号机Semaphore示例

alex线程讲解

(转)Python3入门之线程threading常用方法的更多相关文章

  1. python3下multiprocessing、threading和gevent性能对比----暨进程池、线程池和协程池性能对比

    python3下multiprocessing.threading和gevent性能对比----暨进程池.线程池和协程池性能对比   标签: python3 / 线程池 / multiprocessi ...

  2. python3 线程 threading.Thread GIL性能详解(2.3)

    python3 线程 threading 最基础的线程的使用 import threading, time value = 0 lock = threading.Lock() def change(n ...

  3. Python-----多线程threading用法

    threading模块是Python里面常用的线程模块,多线程处理任务对于提升效率非常重要,先说一下线程和进程的各种区别,如图 概括起来就是 IO密集型(不用CPU) 多线程计算密集型(用CPU) 多 ...

  4. Python 线程(threading) 进程(multiprocessing)

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  5. 【转】Python3 (入门6) 库的打包与安装

    Python3 (入门6) 库的打包与安装 本文由 Luzhuo 编写,转发请保留该信息. 原文: http://blog.csdn.net/Rozol/article/details/6940288 ...

  6. Python3入门机器学习经典算法与应用

    <Python3入门机器学习经典算法与应用> 章节第1章 欢迎来到 Python3 玩转机器学习1-1 什么是机器学习1-2 课程涵盖的内容和理念1-3 课程所使用的主要技术栈第2章 机器 ...

  7. java基础25 线程的常用方法、线程安全问题、死锁现象

    一.线程的常用方法 1.Thread(String name):初始化线程的名字2. setName(String name):设置线程的名字3. getName():返回线程的名字4. sleep( ...

  8. python学习笔记——线程threading (一)

    1 线程threading 1.1 基本概述 也被称为轻量级的进程. 线程是计算机多任务编程的一种方式,可以使用计算机的多核资源. 线程死应用程序中工作的最小单元 1.2 线程特点 (1)进程的创建开 ...

  9. python笔记9-多线程Threading之阻塞(join)和守护线程(setDaemon)

    python笔记9-多线程Threading之阻塞(join)和守护线程(setDaemon) 前言 今天小编YOYO请xiaoming和xiaowang吃火锅,吃完火锅的时候会有以下三种场景: - ...

随机推荐

  1. @RequestBody jackson解析复杂的传入值的一个坑;jackson解析迭代数组;jackson多重数组;jakson数组

    一.实际开发的一个问题. 传入一个json数组,数组中还嵌套数组,运用springboot+Jpa框架,@RequestBody注解传入数据 Controller @ApiOperation(valu ...

  2. HDU 5468 Puzzled Elena (dfs + 莫比乌斯反演)

    题意:给定一棵带权树,求每个点与其子树结点的权值互质的个数. 析:首先先要进行 dfs 遍历,len[i] 表示能够整除 i 的个数,在遍历的前和遍历后的差值就是子树的len值,有了这个值,就可以使用 ...

  3. spring mvc ajax异步文件的上传和普通文件上传

    表单提交方式文件上传和ajax异步文件上传 一:首先是我在spring mvc下的表单提交方式上传 ssm的包配置我就不一一详细列出来了,但是上传的包我还是列出来 这一段我也不知道怎么给大家讲解就是直 ...

  4. 【慕课网实战】Spark Streaming实时流处理项目实战笔记九之铭文升级版

    铭文一级: 核心概念:StreamingContext def this(sparkContext: SparkContext, batchDuration: Duration) = { this(s ...

  5. 上传文件夹+php

    最近公司做工程项目,实现文件夹上传 网上找了很久,发现网上很多代码大都存在很多问题,不过还是让我找到了一个符合要求的项目. 对项目的文件夹上传功能做出分析,找出文件夹上传的原理,对文件夹的传输模式深入 ...

  6. Linux后台开发工具箱

    https://files-cdn.cnblogs.com/files/aquester/Linux后台开发工具箱.pdf 目录 目录 1 1. 前言 3 2. 脚本类工具 3 2.1. sed命令- ...

  7. 20145232 韩文浩 《Java程序设计》第6周学习总结

    教材学习内容总结 Java是以串流(Stream)的方式来处理输入与输出. 串流是一种抽象观念,从键盘输入资料,将处理结果输入档案,以及读取档案的内容等动作皆视为串流的处理. 输入串流代表对象为jav ...

  8. Mapnik

    Downloads Latest Release The latest release is Mapnik v3.0.22.最新版本是Mapnik v3.0.22. Mapnik 3.0.22 Rel ...

  9. QT中的线程与事件循环理解(2)

    1. Qt多线程与Qobject的关系 每一个 Qt 应用程序至少有一个事件循环,就是调用了QCoreApplication::exec()的那个事件循环.不过,QThread也可以开启事件循环.只不 ...

  10. AngularJS 脏检查机制

    脏检查是AngularJS的核心机制之一,它是实现双向绑定.MVVM模式的重要基础. 一.digest循环 AngularJS将双向绑定转换为一个堆watch表达式,然后递归检查这些watch表达式的 ...