一多线程的概念介绍

threading模块介绍

threading模块和multiprocessing模块在使用层面,有很大的相似性。

二、开启多线程的两种方式

 1.创建线程的开销比创建进程的开销小,因而创建线程的速度快
from multiprocessing import Process
from threading import Thread
import os
import time
def work():
print('<%s> is running'%os.getpid())
time.sleep(2)
print('<%s> is done'%os.getpid()) if __name__ == '__main__':
t=Thread(target=work,)
# t= Process(target=work,)
t.start()
print('主',os.getpid()) 开启进程的第一种方式
 from threading import Thread
import time
class Work(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
# time.sleep(2)
print('%s say hell'%self.name)
if __name__ == '__main__':
t = Work('egon')
t.start()
print('主') 开启线程的第二种方式(用类)

在一个进程下开启多个线程与在一个进程下开启多个子进程的区别

 from  multiprocessing import Process
from threading import Thread
import time
def work():
time.sleep(2)
print('hello')
if __name__ == '__main__':
t = Thread(target=work)#如果等上几秒,他会在开启的过程中先打印主,如果不等会先打印hello
# t = Process(target=work) #子进程会先打印主,
t.start()
print('主') 线程的开启速度大于进程的开启速度
 # 2.----------
from multiprocessing import Process
from threading import Thread
import os
def work():
print('hello',os.getpid())
if __name__ == '__main__':
#在主进程下开启多个线程,每个线程都跟主进程的pid一样
t1= Thread(target=work)
t2 = Thread(target=work)
t1.start()
t2.start()
print('主线程pid',os.getpid()) #来多个进程,每个进程都有不同的pid
p1 = Process(target=work)
p2 = Process(target=work)
p1.start()
p2.start()
print('主进程pid', os.getpid()) 在同一个进程下开多个进程和开多个线程的pid的不同
 from  threading import Thread
from multiprocessing import Process
import os
def work():
global n
n-=1
print(n) #所以被改成99了
n = 100
if __name__ == '__main__':
# p = Process(target=work)
p = Thread(target=work) #当开启的是线程的时候,因为同一进程内的线程之间共享进程内的数据
#所以打印的n为99
p.start()
p.join()
print('主',n) #毫无疑问子进程p已经将自己的全局的n改成了0,
# 但改的仅仅是它自己的,查看父进程的n仍然为100 同一进程内的线程共享该进程的数据

进程之间是互相隔离的,不共享。需要借助第三方来完成共享(借助队列,管道,共享数据)

三、练习

练习一:多线程实现并发

 from socket import *
from threading import Thread
s = socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #端口重用
s.bind(('127.0.0.1',8081))
s.listen(5)
print('start running...')
def talk(coon,addr):
while True: # 通信循环
try:
cmd = coon.recv(1024)
print(cmd.decode('utf-8'))
if not cmd: break
coon.send(cmd.upper())
print('发送的是%s'%cmd.upper().decode('utf-8'))
except Exception:
break
coon.close()
if __name__ == '__main__':
while True:#链接循环
coon,addr = s.accept()
print(coon,addr)
p =Thread(target=talk,args=(coon,addr))
p.start()
s.close() 服务端
 from socket import *
c = socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8081))
while True:
cmd = input('>>:').strip()
if not cmd:continue
c.send(cmd.encode('utf-8'))
data = c.recv(1024)
print('接受的是%s'%data.decode('utf-8'))
c.close() 客户端

练习二:三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件

 from threading import Thread
import os
input_l = []
format_l = []
def talk(): #监听输入任务
while True:
cmd = input('>>:').strip()
if not cmd:continue
input_l.append(cmd) def format():
while True:
if input_l:
res = input_l.pop()#取出来
format_l.append(res.upper()) #取出来后变大写
def save():
while True:
if format_l: #如果format_l不为空
with open('db','a') as f:
f.write(format_l.pop()+'\n') #写进文件
f.flush()
if __name__ == '__main__':
t1=Thread(target=talk)
t2=Thread(target=format)
t3=Thread(target=save)
t1.start()
t2.start()
t3.start() 答案

四、多线程共享同一个进程内的地址空间 

 from threading import Thread
from multiprocessing import Process
import os
n = 100
def talk():
global n
n-=100
print(n)
if __name__ == '__main__':
t = Thread(target=talk) #如果开启的是线程的话,n=0
# t = Process(target=talk) #如果开启的是进程的话,n=100
t.start()
t.join()
print('主',n)

五、线程对象的其他属性和方法

 Thread实例对象的方法
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。 threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
 from  threading import Thread
from multiprocessing import Process
import time,os,threading
def work():
time.sleep(2)
print('%s is running' % threading.currentThread().getName())
print(threading.current_thread()) #其他线程
print(threading.currentThread().getName()) #得到其他线程的名字
if __name__ == '__main__':
t = Thread(target=work)
t.start() print(threading.current_thread().getName()) #主线程的名字
print(threading.current_thread()) #主线程
print(threading.enumerate()) #连同主线程在内有两个运行的线程
time.sleep(2)
print(t.is_alive()) #判断线程是否存活
print(threading.activeCount())
print('主') 线程的其他属性和方法

六、join与守护线程

主进程等所有的非守护的子进程结束他才结束(回收它子进程的资源):(有父子关系)
主线程等非守护线程全都结束它才结束: (没父子关系)

 from  threading import Thread
import time,os
def talk():
time.sleep(3)
print('%s is running..'%os.getpid())
if __name__ == '__main__':
t = Thread(target=talk)
t.start()
t.join() #主进程在等子进程结束
print('主') join

守护线程与守护进程的区别

1.守护进程:主进程会等到所有的非守护进程结束,才销毁守护进程。也就是说(主进程运行完了被守护的那个就干掉了)

2.守护线程:主线程运行完了守护的那个还没有干掉,主线程等非守护线程全都结束它才结束

 from  multiprocessing import Process
from threading import Thread,currentThread
import time,os
def talk1():
time.sleep(2)
print('hello')
def talk2():
time.sleep(2)
print('you see see')
if __name__ == '__main__':
t1 = Thread(target=talk1)
t2 = Thread(target=talk2)
# t1 = Process(target=talk1)
# t2 = Process(target=talk2)
t1.daemon = True
t1.start()
t2.start()
print('主线程',os.getpid()) 守护进程和守护线程
 #3 --------迷惑人的例子
from threading import Thread
import time
def foo():
print(123)
# time.sleep(10) #如果这个等的时间大于下面等的时间,就把不打印end123了
time.sleep(2) #如果这个等的时间小于下面等的时间,就把end123也打印了
print('end123')
def bar():
print(456)
# time.sleep(5)
time.sleep(10)
print('end456')
if __name__ == '__main__':
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.daemon = True #主线程运行完了守护的那个还没有干掉,
# 主线程等非守护线程全都结束它才结束
t1.start()
t2.start()
print('main---------') 一个诱惑人的例子

七、GIL与Lock

1.python GIL(Global Interpreter Lock) #全局的解释器锁

2.锁的目的:牺牲了效率,保证了数据的安全
3.保护不同的数据加不同的锁()
4.python自带垃圾回收

5.谁拿到GIL锁就让谁得到Cpython解释器的执行权限

6.GIT锁保护的是Cpython解释器数据的安全,而不会保护你自己程序的数据的安全
7.GIL锁当遇到阻塞的时候,就被迫的吧锁给释放了,那么其他的就开始抢锁了,抢到
后吧值修改了,但是第一个拿到的还在原本拿到的那个数据的那停留着呢,当再次拿
到锁的时候,数据已经修改了,而你还拿的原来的,这样就混乱了,所以也就保证不了
数据的安全了。
8.那么怎么解决数据的安全ne ?
自己再给加吧锁:mutex=Lock()

 同步锁

GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock

过程分析:所有线程抢的是GIL锁,或者说所有线程抢的是执行权限

  线程1抢到GIL锁,拿到执行权限,开始执行,然后加了一把Lock,还没有执行完毕,即线程1还未释放Lock,有可能线程2抢到GIL锁,开始执行,执行过程中发现Lock还没有被线程1释放,于是线程2进入阻塞,被夺走执行权限,有可能线程1拿到GIL,然后正常执行到释放Lock。。。这就导致了串行运行的效果

  既然是串行,那我们执行

  t1.start()

  t1.join

  t2.start()

  t2.join()

  这也是串行执行啊,为何还要加Lock呢,需知join是等待t1所有的代码执行完,相当于锁住了t1的所有代码,而Lock只是锁住一部分操作共享数据的代码。

因为Python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题, 这可以说是Python早期版本的遗留问题。 

 from threading import Thread,Lock
import time
n=100
def work():
mutex.acquire()
global n
temp=n
time.sleep(0.01)
n=temp-1
mutex.release()
if __name__ == '__main__':
mutex=Lock()
t_l=[]
s=time.time()
for i in range(100):
t=Thread(target=work)
t_l.append(t)
t.start()
for t in t_l:
t.join()
print('%s:%s' %(time.time()-s,n)) 全局解释锁

锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁:

 import threading
mutex = threading.Lock()
mutex.aquire()
'''
对公共数据的操作
'''
mutex.release() 锁的格式
 分析:
2   1.100个线程去抢GIL锁,即抢执行权限
3 2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
4 3. 极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
5 4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程

如果不加锁:并发执行,速度快,数据不安全。

加锁:串行执行,速度慢,数据安全。

 #不加锁:并发执行,速度快,数据不安全
from threading import current_thread,Thread,Lock
import os,time
def task():
global n
print('%s is running' %current_thread().getName())
temp=n
time.sleep(0.5)
n=temp-1 if __name__ == '__main__':
n=100
lock=Lock()
threads=[]
start_time=time.time()
for i in range(100):
t=Thread(target=task)
threads.append(t)
t.start()
for t in threads:
t.join() stop_time=time.time()
print('主:%s n:%s' %(stop_time-start_time,n)) '''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:0.5216062068939209 n:99
''' #不加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全
from threading import current_thread,Thread,Lock
import os,time
def task():
#未加锁的代码并发运行
time.sleep(3)
print('%s start to run' %current_thread().getName())
global n
#加锁的代码串行运行
lock.acquire()
temp=n
time.sleep(0.5)
n=temp-1
lock.release() if __name__ == '__main__':
n=100
lock=Lock()
threads=[]
start_time=time.time()
for i in range(100):
t=Thread(target=task)
threads.append(t)
t.start()
for t in threads:
t.join()
stop_time=time.time()
print('主:%s n:%s' %(stop_time-start_time,n)) '''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:53.294203758239746 n:0
''' #有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
#没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
#start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
#单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
from threading import current_thread,Thread,Lock
import os,time
def task():
time.sleep(3)
print('%s start to run' %current_thread().getName())
global n
temp=n
time.sleep(0.5)
n=temp-1 if __name__ == '__main__':
n=100
lock=Lock()
start_time=time.time()
for i in range(100):
t=Thread(target=task)
t.start()
t.join()
stop_time=time.time()
print('主:%s n:%s' %(stop_time-start_time,n)) '''
Thread-1 start to run
Thread-2 start to run
......
Thread-100 start to run
主:350.6937336921692 n:0 #耗时是多么的恐怖
''' 互斥锁与join的区别(重点!!!)

利用threading模块开线程的更多相关文章

  1. Python之网路编程利用threading模块开线程

    一多线程的概念介绍 threading模块介绍 threading模块和multiprocessing模块在使用层面,有很大的相似性. 二.开启多线程的两种方式 1 1.创建线程的开销比创建进程的开销 ...

  2. Python使用Threading模块创建线程

    使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法: #!/usr/bin/python # -*- coding: UTF-8 ...

  3. 使用threading模块创建线程

    #_author:来童星#date:2019/12/17#使用threading模块创建线程import threading,timedef process(): for i in range(3): ...

  4. Python——threading模块(线程)

    一.threading模块的对象 Thread:表示一个执行线程的对象 Lock:锁 Rlock:可重入锁对象 Condition:条件变量对象,使得一个线程等待另一个线程满足特定的“条件” Even ...

  5. threading模块创建线程

    什么是线程 (thread) 线程也是一种多任务编程方式,可以使用计算机的多核资源.线程被称为轻量级的进程. 线程特征 *线程计算机多核分配的最小单位 *一个进程可以包含多个线程 *线程也是一个运行的 ...

  6. <python的线程与threading模块>

    <python的线程与threading模块> 一 线程的两种调用方式 threading 模块建立在thread 模块之上.thread模块以低级.原始的方式来处理和控制线程,而thre ...

  7. Python:使用threading模块实现多线程编程

    转:http://blog.csdn.net/bravezhe/article/details/8585437 Python:使用threading模块实现多线程编程一[综述] Python这门解释性 ...

  8. python学习笔记之使用threading模块实现多线程(转)

    综述 Python这门解释性语言也有专门的线程模型,Python虚拟机使用GIL(Global Interpreter Lock,全局解释器锁)来互斥线程对共享资源的访问,但暂时无法利用多处理器的优势 ...

  9. Python之threading模块的使用

    作用:同一个进程空间并发运行多个操作,专业术语简称为:[多线程] 1.任务函数不带参数多线程 #!/usr/bin/env python # -*- coding: utf-8 -*- import ...

随机推荐

  1. 分清<url-pattern>/</url-pattern>与<url-pattern>/*</url-pattern>的不同

    在写springMVC配置web.xml的时候会碰到下面有时候写/,有的时候又写/: 那么这两者有什么区别呢?我现在进行一些讲解: 1.当配置<url-pattern>/</url- ...

  2. Python数据类型详解——元组

    Python数据类型详解--元组 有时候我们的列表数据不想被别人修改时该怎么办? 此时,就可以使用元组来存放,元祖又称为只读列表,不能修改 定义方式:与列表类似,将列表的[]换成()即可. 特性: 1 ...

  3. Eclipse Paho MQTT Utility

    下载地址: https://repo.eclipse.org/content/repositories/paho-releases/org/eclipse/paho/org.eclipse.paho. ...

  4. bzoj 1085骑士精神

    bzoj 1085骑士精神 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士,且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2 ...

  5. codeforces 14E. Camels(多维dp)

    题目链接:http://codeforces.com/problemset/problem/14/E 题意:就是给出n个点要求画出t个波峰和t-1个波谷 很显然要t个波峰和t-1个波谷开始是波动一定是 ...

  6. hdu 1671 Phone List 字典树模板

    Given a list of phone numbers, determine if it is consistent in the sense that no number is the pref ...

  7. 高可用的K8S集群部署方案

    涉及到的内容 LVS HAProxy Harbor etcd Kubernetes (Master Worker) 整体拓补图 以上是最小生产可用的整体拓补图(相关节点根据需要进行增加,但不能减少) ...

  8. CSS3 04. 伸缩布局、设置主轴,侧轴方向、主/侧轴对齐方式、 伸缩比例、元素换行、换行控制、覆盖父元素的align-items;控制子元素顺序、web字体、突变字体

    CSS3 在布局方面做了非常大的改进,对块级元素的布局排列变得十分灵活,适应性非常强,其强大的伸缩性,在响应式开发中可以发挥极大的作用.(兼容性不好) 必要元素: 指定一个盒子为伸缩盒子 displa ...

  9. 2019icpc徐州网络赛_I_query

    题意 给定一个序列,多次询问区间\([l,r]\)中满足\(min(a[i],a[j])==gcd(a[i],a[j])\)的数对\((i,j)\)数. 分析 其实就是求区间有倍数关系的数对数. 由于 ...

  10. Mac破解软件下载的几个网站

    一.关于破解(盗版)软件的个人看法(可忽略,网址在文末): 1.在经济(预算)允许的范围内,尽量支持正版: 2.软件如衣服,在不同的季节,不同的店铺买价格不一样,国内的一些代理网站经常会打折: 3.作 ...