一、什么是线程?
线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程。车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
总结进程与线程区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
''' 进程:资源单位 线程:执行单位 线程才是真正干活的人,干活中需要的资源由线程所在进程提供 每个进程肯定自带一个线程 每个进程内可创建多个线程 ''' ''' 开进程: 申请空间 拷贝代码 消耗资源大 开线程: 同一个进程内创建多个线程,无需上述两种操作,消耗资源相对较小 ''' |
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
二、开启线程的两种方式
1、方式1
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from threading import Thread import time # 方法一 def task(name): print ( '%s is running' % name) time.sleep( 1 ) print ( '%s is over' % name) # 开启线程不需要在main下面执行代码,直接书写就可以 # 但是习惯性的将启动命令写在main下面 t = Thread(target = task, args = ( 'egon' ,)) t.start() # 创建线程的开销非常小,几乎是代码一执行就已经创建了 print ( '主' ) ''' |
运行结果:
egon is running
主
egon is over
'''
2、方式2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from threading import Thread class MyThread(Thread): def __init__( self , name): # 重写了别人的方法,又不知道别人的方法里有啥,就调用父类的方法 super ().__init__() self .name = name def run( self ): print ( '%s is running' % self .name) time.sleep( 1 ) print ( '%s is over' % self .name) if __name__ = = '__main__' : t = MyThread( 'egon' ) t.start() print ( '主' ) ''' |
运行结果:
egon is running
主
egon is over
'''
三、线程对象的jion方法()
看过我讲解进程文章的小伙伴想必都知道jion的功能,线程的jion方法于进程的jion方法功能类似-等待一个线程执行完毕后再执行下一个线程
1
2
3
4
5
6
7
8
9
10
|
from threading import Thread def task(name): print ( '%s is running' % name) time.sleep( 1 ) print ( '%s is over' % name) if __name__ = = '__main__' : t = Thread(target = task,args = ( 'egon' ,)) t.start() t.join() # 主线程等待子线程运行结束后再执行 print ( '主' ) |
'''
运行结果:
egon is running
egon is over
主
'''
补充一个知识点:同一个进程下的多个线程数据共享,下面为大家举一个简单的案例
1
2
3
4
5
6
7
8
9
|
from threading import Thread money = 100 def task(): global money money = 66 if __name__ = = '__main__' : t = Thread(target = task,args = ()) t.start() print (money) |
# 结果:66
四、 补充小案例
1
2
3
4
5
6
7
8
9
|
from threading import Thread import os,time def task(): print ( '子 pid:' ,os.getpid()) if __name__ = = '__main__' : t = Thread(target = task,args = ()) t.start() print ( '主 pid:' ,os.getpid()) # 两个线程的pid号一样,说明在同一个进程下 |
'''
运行结果:
子 pid: 13444
主 pid: 13444
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 这是个容易混淆的案例 from threading import Thread,current_thread,active_count import os,time def task(n): print ( '子' ,current_thread().name) time.sleep(n) # 延长线程存活时间 if __name__ = = '__main__' : t = Thread(target = task,args = ( 1 ,)) t1 = Thread(target = task,args = ( 1 ,)) t.start() t1.start() t.join() # print('主',current_thread().name)# 获取线程名字 print (active_count()) # 统计当前活跃的进程数 |
'''
运行结果:
子 Thread-1
子 Thread-2
1
'''
# 这里大家容易以为是3,其实运行后只有一个线程在活跃了,其它两个线程运行完后就停止运行了
五、守护线程
守护线程与守护进程的概念也类似,其实大家也能注意到,进程与线程有许多知识点即用法都是相通的,理解了一个另一个也是差不多的道理
1、守护线程会随着主线程的结束而结束
2、主线程运行结束后不会立刻结束,会等待所有的其它非守护线程结束后才会结束
3、因为主线程的结束意味着所在进程的结束
1
2
3
4
5
6
7
8
9
10
11
|
from threading import Thread import time def task(name): print ( '%s is running' % name) time.sleep( 1 ) print ( '%s is over' % name) if __name__ = = '__main__' : t = Thread(target = task,args = ( 'egon' ,)) t.daemon = True #将t设置为守护线程 t.start() print ( '主' ) |
'''
运行结果:
egon is running
主
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 稍微有点迷惑性的例子 from threading import Thread import time def foo(): print ( '1234' ) time.sleep( 1 ) print ( 'end1234' ) def func(): print ( '5678' ) time.sleep( 3 ) print ( 'end5678' ) if __name__ = = '__main__' : t1 = Thread(target = foo,args = ()) t2 = Thread(target = func,args = ()) t1.daemon = True # t1设为守护线程,t2为非守护线程 t1.start() t2.start() print ( '主......' ) |
'''
运行结果:
1234
5678主......
end1234
end5678
'''
'''
因主线程会等待非守护线程运行结束后在结束,
所有主线程会等待t2(非守护线程)结束再结束,
'''
六、线程互斥锁
多个线程操作同一份数据的时候,会出现数据错乱的问题
针对上述问题,解决方式就是加锁处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
from threading import Thread,Lock import time money = 100 mutex = Lock() def task(): global money mutex.acquire() tmp = money time.sleep( 0.1 ) # 模拟网络延迟 money = tmp - 1 mutex.release() if __name__ = = '__main__' : t_list = [] for i in range ( 100 ): t = Thread(target = task,args = ()) t.start() t_list.append(t) for t in t_list: t.join() print (money) |
# 运行结果:0
# 多个人操作同一份数据,数据错乱,加锁处理
七、GTL-全局解释器
相信学python的小伙伴都知道,python解释器其实有多个版本
- Cpython
- Jpython
- Pypython
但是普遍使用的都是Cpython解释器
在Cpython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时执行
要注意同一进程下的多个线程无法利用多核优势!!!!
想必大家心中也有不少疑惑:pyhon的多线程是不是一点用都没了呢????
因为Cpython中的内存管理不是线程安全的。多线程并不是一无是处的,在遇到多IO操作的时候,多核的优势也会显示不出来,多进程与多线程的效率在该情况下差不了多少,而此时多进程相对浪费资源,多线程更加节省资源
ps:内存管理就是垃圾回收机制:
1、引用计数
2、标记清除
3、分带回收
1
2
3
4
5
6
|
# GTL-全局解释器 # 重点:1、GIL不是python的特点而是Cpython解释器的特点 # 2、GIL是保证解释器级别的数据的安全 # 3、GIL会导致同一个进程下的多个线程无法同时进行(即无法利用多核优势) # 4、针对不同的数据还是需要加不同的锁处理 # 5、解释型语言的通病,同一个进程下多个线程无法利用多核优势 |
多线程是否有用要看具体情况
八、验证多线程与多线程运用场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 计算密集型(CPU一直工作,也没有IO)(更适合多进程) from multiprocessing import Process from threading import Thread import os,time # 多进程情况 def work(): res = 0 for i in range ( 0 , 10000000 ): res * = i if __name__ = = '__main__' : l = [] print (os.cpu_count()) # 获取当前计算机CPU核数 start_time = time.time() for i in range ( 8 ): # 我计算机是8核 p = Process(target = work,args = ()) p.start() l.append(p) for p in l: p.join() print (time.time() - start_time) |
'''
运行结果:
8
2.0726492404937744
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 多线程情况 from multiprocessing import Process from threading import Thread import os,time def work(): res = 0 for i in range ( 0 , 10000000 ): res * = i if __name__ = = '__main__' : l = [] print (os.cpu_count()) # 获取当前计算机CPU核数 start_time = time.time() for i in range ( 8 ): # 我计算机是8核 t = Thread(target = work,args = ()) t.start() l.append(t) for p in l: p.join() print (time.time() - start_time) |
'''
运行结果:
8
3.5790603160858154
'''
# 显然可知:计算密集型更时候多进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# IO密集型(任务一直有IO)(多线程更合适) from multiprocessing import Process from threading import Thread import os,time # 多线程 def work(): time.sleep( 1 ) if __name__ = = '__main__' : l = [] start_time = time.time() for i in range ( 40 ): t = Thread(target = work,args = ()) t.start() l.append(t) for p in l: p.join() print (time.time() - start_time) # 运行结果:1.0205152034759521 # 多进程 from multiprocessing import Process from threading import Thread import os,time def work(): time.sleep( 1 ) if __name__ = = '__main__' : l = [] start_time = time.time() for i in range ( 40 ): p = Process(target = work,args = ()) # t=Thread(target=work,args=()) # t.start() # l.append(t) p.start() l.append(p) for p in l: p.join() print (time.time() - start_time) |
# 运行结果:5.927189588546753
# 显然可知:IO密集型更适合多线程
总结:
多线程和多进程都各自有各自的优势
并且在后面的项目中通常可以多进程下面再开设多线程
这样的话我们可以利用多核也可以节省资源消耗
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!
原文链接:https://blog.csdn.net/m0_51734025/article/details/121597348