一多线程的概念介绍

threading模块介绍

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

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

1 1.创建线程的开销比创建进程的开销小,因而创建线程的速度快
2 from multiprocessing import Process
3 from threading import Thread
4 import os
5 import time
6 def work():
7 print('<%s> is running'%os.getpid())
8 time.sleep(2)
9 print('<%s> is done'%os.getpid())
10
11 if __name__ == '__main__':
12 t=Thread(target=work,)
13 # t= Process(target=work,)
14 t.start()
15 print('主',os.getpid())

开启进程的第一种方式

 1 from threading import Thread
2 import time
3 class Work(Thread):
4 def __init__(self,name):
5 super().__init__()
6 self.name = name
7 def run(self):
8 # time.sleep(2)
9 print('%s say hell'%self.name)
10 if __name__ == '__main__':
11 t = Work('egon')
12 t.start()
13 print('主')

开启线程的第二种方式(用类)

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

 1 from  multiprocessing import Process
2 from threading import Thread
3 import time
4 def work():
5 time.sleep(2)
6 print('hello')
7 if __name__ == '__main__':
8 t = Thread(target=work)#如果等上几秒,他会在开启的过程中先打印主,如果不等会先打印hello
9 # t = Process(target=work) #子进程会先打印主,
10 t.start()
11 print('主')

线程的开启速度大于进程的开启速度

 1 # 2.----------
2 from multiprocessing import Process
3 from threading import Thread
4 import os
5 def work():
6 print('hello',os.getpid())
7 if __name__ == '__main__':
8 #在主进程下开启多个线程,每个线程都跟主进程的pid一样
9 t1= Thread(target=work)
10 t2 = Thread(target=work)
11 t1.start()
12 t2.start()
13 print('主线程pid',os.getpid())
14
15 #来多个进程,每个进程都有不同的pid
16 p1 = Process(target=work)
17 p2 = Process(target=work)
18 p1.start()
19 p2.start()
20 print('主进程pid', os.getpid())

在同一个进程下开多个进程和开多个线程的pid的不同

 1 from  threading import Thread
2 from multiprocessing import Process
3 import os
4 def work():
5 global n
6 n-=1
7 print(n) #所以被改成99了
8 n = 100
9 if __name__ == '__main__':
10 # p = Process(target=work)
11 p = Thread(target=work) #当开启的是线程的时候,因为同一进程内的线程之间共享进程内的数据
12 #所以打印的n为99
13 p.start()
14 p.join()
15 print('主',n) #毫无疑问子进程p已经将自己的全局的n改成了0,
16 # 但改的仅仅是它自己的,查看父进程的n仍然为100

同一进程内的线程共享该进程的数据

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

三、练习

练习一:多线程实现并发

 1 from socket import *
2 from threading import Thread
3 s = socket(AF_INET,SOCK_STREAM)
4 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #端口重用
5 s.bind(('127.0.0.1',8081))
6 s.listen(5)
7 print('start running...')
8 def talk(coon,addr):
9 while True: # 通信循环
10 try:
11 cmd = coon.recv(1024)
12 print(cmd.decode('utf-8'))
13 if not cmd: break
14 coon.send(cmd.upper())
15 print('发送的是%s'%cmd.upper().decode('utf-8'))
16 except Exception:
17 break
18 coon.close()
19 if __name__ == '__main__':
20 while True:#链接循环
21 coon,addr = s.accept()
22 print(coon,addr)
23 p =Thread(target=talk,args=(coon,addr))
24 p.start()
25 s.close()

服务端

 1 from socket import *
2 c = socket(AF_INET,SOCK_STREAM)
3 c.connect(('127.0.0.1',8081))
4 while True:
5 cmd = input('>>:').strip()
6 if not cmd:continue
7 c.send(cmd.encode('utf-8'))
8 data = c.recv(1024)
9 print('接受的是%s'%data.decode('utf-8'))
10 c.close()

客户端

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

 1 from threading import Thread
2 import os
3 input_l = []
4 format_l = []
5 def talk(): #监听输入任务
6 while True:
7 cmd = input('>>:').strip()
8 if not cmd:continue
9 input_l.append(cmd)
10
11 def format():
12 while True:
13 if input_l:
14 res = input_l.pop()#取出来
15 format_l.append(res.upper()) #取出来后变大写
16 def save():
17 while True:
18 if format_l: #如果format_l不为空
19 with open('db','a') as f:
20 f.write(format_l.pop()+'\n') #写进文件
21 f.flush()
22 if __name__ == '__main__':
23 t1=Thread(target=talk)
24 t2=Thread(target=format)
25 t3=Thread(target=save)
26 t1.start()
27 t2.start()
28 t3.start()

答案

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

 1 from threading import Thread
2 from multiprocessing import Process
3 import os
4 n = 100
5 def talk():
6 global n
7 n-=100
8 print(n)
9 if __name__ == '__main__':
10 t = Thread(target=talk) #如果开启的是线程的话,n=0
11 # t = Process(target=talk) #如果开启的是进程的话,n=100
12 t.start()
13 t.join()
14 print('主',n)

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

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

线程的其他属性和方法

六、join与守护线程

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

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

join

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

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

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

 1 from  multiprocessing import Process
2 from threading import Thread,currentThread
3 import time,os
4 def talk1():
5 time.sleep(2)
6 print('hello')
7 def talk2():
8 time.sleep(2)
9 print('you see see')
10 if __name__ == '__main__':
11 t1 = Thread(target=talk1)
12 t2 = Thread(target=talk2)
13 # t1 = Process(target=talk1)
14 # t2 = Process(target=talk2)
15 t1.daemon = True
16 t1.start()
17 t2.start()
18 print('主线程',os.getpid())

守护进程和守护线程

 1 #3 --------迷惑人的例子
2 from threading import Thread
3 import time
4 def foo():
5 print(123)
6 # time.sleep(10) #如果这个等的时间大于下面等的时间,就把不打印end123了
7 time.sleep(2) #如果这个等的时间小于下面等的时间,就把end123也打印了
8 print('end123')
9 def bar():
10 print(456)
11 # time.sleep(5)
12 time.sleep(10)
13 print('end456')
14 if __name__ == '__main__':
15 t1 = Thread(target=foo)
16 t2 = Thread(target=bar)
17 t1.daemon = True #主线程运行完了守护的那个还没有干掉,
18 # 主线程等非守护线程全都结束它才结束
19 t1.start()
20 t2.start()
21 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早期版本的遗留问题。 

 1 from threading import Thread,Lock
2 import time
3 n=100
4 def work():
5 mutex.acquire()
6 global n
7 temp=n
8 time.sleep(0.01)
9 n=temp-1
10 mutex.release()
11 if __name__ == '__main__':
12 mutex=Lock()
13 t_l=[]
14 s=time.time()
15 for i in range(100):
16 t=Thread(target=work)
17 t_l.append(t)
18 t.start()
19 for t in t_l:
20 t.join()
21 print('%s:%s' %(time.time()-s,n))

全局解锁

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

1 import threading
2 mutex = threading.Lock()
3 mutex.aquire()
4 '''
5 对公共数据的操作
6 '''
7 mutex.release()

锁的格式

1 分析:
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的过程

GIL锁和互斥锁综合分析(重点)

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

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

 1 #不加锁:并发执行,速度快,数据不安全
2 from threading import current_thread,Thread,Lock
3 import os,time
4 def task():
5 global n
6 print('%s is running' %current_thread().getName())
7 temp=n
8 time.sleep(0.5)
9 n=temp-1
10
11
12 if __name__ == '__main__':
13 n=100
14 lock=Lock()
15 threads=[]
16 start_time=time.time()
17 for i in range(100):
18 t=Thread(target=task)
19 threads.append(t)
20 t.start()
21 for t in threads:
22 t.join()
23
24 stop_time=time.time()
25 print('主:%s n:%s' %(stop_time-start_time,n))
26
27 '''
28 Thread-1 is running
29 Thread-2 is running
30 ......
31 Thread-100 is running
32 主:0.5216062068939209 n:99
33 '''
34
35
36 #不加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全
37 from threading import current_thread,Thread,Lock
38 import os,time
39 def task():
40 #未加锁的代码并发运行
41 time.sleep(3)
42 print('%s start to run' %current_thread().getName())
43 global n
44 #加锁的代码串行运行
45 lock.acquire()
46 temp=n
47 time.sleep(0.5)
48 n=temp-1
49 lock.release()
50
51 if __name__ == '__main__':
52 n=100
53 lock=Lock()
54 threads=[]
55 start_time=time.time()
56 for i in range(100):
57 t=Thread(target=task)
58 threads.append(t)
59 t.start()
60 for t in threads:
61 t.join()
62 stop_time=time.time()
63 print('主:%s n:%s' %(stop_time-start_time,n))
64
65 '''
66 Thread-1 is running
67 Thread-2 is running
68 ......
69 Thread-100 is running
70 主:53.294203758239746 n:0
71 '''
72
73 #有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
74 #没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
75 #start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
76 #单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
77 from threading import current_thread,Thread,Lock
78 import os,time
79 def task():
80 time.sleep(3)
81 print('%s start to run' %current_thread().getName())
82 global n
83 temp=n
84 time.sleep(0.5)
85 n=temp-1
86
87
88 if __name__ == '__main__':
89 n=100
90 lock=Lock()
91 start_time=time.time()
92 for i in range(100):
93 t=Thread(target=task)
94 t.start()
95 t.join()
96 stop_time=time.time()
97 print('主:%s n:%s' %(stop_time-start_time,n))
98
99 '''
100 Thread-1 start to run
101 Thread-2 start to run
102 ......
103 Thread-100 start to run
104 主:350.6937336921692 n:0 #耗时是多么的恐怖
105 '''

互斥锁与join的区别(重点!!!)

Python之网路编程利用threading模块开线程的更多相关文章

  1. 利用threading模块开线程

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

  2. Python之网路编程利用multiprocessing开进程

    一.multiprocessing模块介绍 python中的多线程无法利用CPU资源,在python中大部分情况使用多进程.python中提供了非常好的多进程包multiprocessing. mul ...

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

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

  4. Python黑帽编程2.6 模块

    Python黑帽编程2.6 模块 我们已经学习了如何在你的程序中定义一次函数而重用代码.如果你想要在其他程序中重用很多函数,那么你该如何编写程序呢?你可能已经猜到了,答案是使用模块.模块基本上就是一个 ...

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

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

  6. python标准库介绍——31 threading 模块详解

    threading 模块 (可选) ``threading`` 模块为线程提供了一个高级接口, 如 [Example 3-1 #eg-3-1] 所示. 它源自 Java 的线程实现. 和低级的 ``t ...

  7. 使用threading模块创建线程

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

  8. Python之网路编程之死锁,递归锁,信号量,Event事件,线程Queue

    一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统 ...

  9. python进阶笔记 thread 和 threading模块学习

    Python通过两个标准库thread和threading提供对线程的支持.thread提供了低级别的.原始的线程以及一个简单的锁.threading基于Java的线程模型设计.锁(Lock)和条件变 ...

随机推荐

  1. select框动态添加选项

    $.ajax({ url : "${staticServer }/ywgl/zkpzgl/zkfkgl/showBillType.htm", //ajax请求路径 type : & ...

  2. Java学习之==>IO文件操作体系

    一.概述 在整个 Java.io 中最重要的就是5个类和一个接口.5个类指的是 File.InputStream.OutputStream.Reader.Writer,一个接口指的是Serializa ...

  3. Word2007—如何快速取消自动编号

    有时候自动编号很麻烦,有没有好的办法可以快速取消呢. 1.禁止自动编号.在Word为其自动加上编号时,只要按下Ctrl+Z键撤销操作,此时自动编号会消失,而且每次键入数字时,该功能就会被禁止了. 2. ...

  4. 封装cookie,自定义过期时间,domain,path

    在使用Cookie进行存储的时候,遇到了许多不可思议的bug,特地标识出来,以作总结. 是这样一个项目,登录是放在官网进行操作的,而登录进入的是后台,后台和官网属于同一域名的不同目录,那么常规进行co ...

  5. 【VS开发】模态对话框和非模态对话框

    MFC中对话框有两种形式,一个是模态对话框(model dialog box),一个是非模态对话框(modeless dialog box). 一.模态对话框(model dialog box) 在程 ...

  6. Vector和ArrayList的区别联系,Hashtable和HashMap的区别联系

    Vector.Hashtable是早期的集合类,线程安全,但是效率低下,被相同原理.结构的ArrayList.HashMap取代. 1.Vector和ArrayList的区别和联系: 联系:实现原理相 ...

  7. python中函数的参数和返回值

    目录 函数 目标 01. 函数参数和返回值的作用 1.1 无参数,无返回值 1.2 无参数,有返回值 1.3 有参数,无返回值 1.4 有参数,有返回值 02. 函数的返回值 进阶 示例 -- 温度和 ...

  8. TableLayoutPanel

    1.简单属性 百度经验:https://jingyan.baidu.com/article/9113f81b7966df2b3314c775.html Name属性:tableLayoutPanel经 ...

  9. linux 内核数据结构之红黑树.

    转载: http://www.cnblogs.com/haippy/archive/2012/09/02/2668099.html https://zh.wikipedia.org/zh/%E7%BA ...

  10. 09: mysql基础面试题

    1.uuid和id区别 1)uuid类型是varchar(36),而自增长Id则一般是bigInt类型. 2)相对于bigInt类型的自增长Id,varchar(36)类型的uuid消耗的物理空间更为 ...