python的并发执行(多线程和多进程的简单使用)

引用自:https://blog.csdn.net/qq_37924224/article/details/105866342

前言
运行代码有的时候就像工厂生产东西,我们程序员可以看成厂长,如果你希望自己的这个厂里多加几条产线,或者多开几个厂,从而提高产量或者说提高效率。(写完吐槽:这个厂长真苦逼,光杆司令啥都要亲自做,手下还动不动罢工,太难了)
那么这个专栏应该就能帮到你,这个专栏是我个人对python和OpenCV的代码加速的个人汇总,链接如下:https://blog.csdn.net/qq_37924224/category_9852410.html
肯定是有用的东西,会定期去更新和维护,建议大家点个订阅和
收藏。如果你也是使用python和OpenCV的,那么这个建议就是强烈建议了。

多线程和多进程会分为四篇博客写完,第一篇也就是这一篇会简单介绍多线程和多进程,以及一些基础的运用。第二篇主讲多线程,第三篇主讲多进程,第四篇再总结提升。OK,我们开始咯!(写完吐槽:MD!不是简单介绍吗,咋写这么多)

目录
python的并发执行(多线程和多进程的简单使用)(针对小白1)
前言
多线程和多进程的介绍
定义
选择的多线程还是多进程的建议(仅针对python)
多线程的简单使用(易懂的代码)
多进程的简单使用(易懂的代码)
关于python中的多线程和多进程的补充说明
技术细节
并行计算的陷阱(坑)
全局锁(GIL)The Global Interpreter Lock
差异,优点和缺点
结束语
多线程和多进程的介绍
定义
进程(Process):进程是正在执行的计算机程序的实例。每个进程都有自己的存储空间,用于存储正在运行的指令,以及用于需要存储和访问任何数据。
线程(Thread):线程是进程的组成部分,可以并行运行。一个进程中可以有多个线程,它们共享相同的内存空间,即父进程的内存空间。这意味着要执行的代码以及程序中声明的所有变量将由所有线程共享。
关系如下图


选择的多线程还是多进程的建议(仅针对python)
以下的选择建议仅针对python:(原因在最后)
CPU密集型(多进程):程序需要占用CPU进行大量的运算和数据处理;
I/O密集型(多线程):程序中需要频繁的进行I/O操作;例如网络中socket数据传输和读取等;
由于python多线程并不是并行执行,因此较适合与I/O密集型程序,多进程并行执行适用于CPU密集型程序
(写完吐槽:因为要写这篇文章看了不少博客,关于CPU密集型的建议,我看到了两种。有的建议多线程,有的建议多进程。但如果你用的是python这个语言,笔者这里建议还是上面我写的那样。)

多线程的简单使用(易懂的代码)
使用threading模块或者_thread模块
简单的使用可以参考:https://www.runoob.com/python3/python3-multithreading.html
这里拿出一些简单的例子
一是使用_thread

#导入需要用到的库
import _thread
import time
import threading

#需要被多线程执行的函数
def print_number(n):
time.sleep(3)
print(n)

#第一种方法:可以调用_thread.start_new_thread ( function, args[, kwargs] )
t1=time.time()
for i in range(5):
_thread.start_new_thread( print_number, (i,) )
t2=time.time()
print("花费的时间:"+str(t2-t1))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
结果如下图
我们可以看到开启线程后,会继续往下执行

二是使用threading

#导入需要用到的库
import _thread
import time
import threading

#需要被多线程执行的函数
def print_number(n):
time.sleep(3)
print(n)

#第二种方法:可以调用threading
t1=time.time()
for i in range(5):
t_thread = threading.Thread(target=print_number,args=(i,))
t_thread.start()
t2=time.time()
print("花费的时间:"+str(t2-t1))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
结果如下图


多进程的简单使用(易懂的代码)
使用multiprocessing模块,使用方法类似于上面的threading模块。(只是使用方法类似哈,别的都不怎么类似)

#导入需要用到的库
import time
import multiprocessing

#需要被多进程执行的函数
def print_number(n):
time.sleep(3)
print(n)

#调用multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})
t1=time.time()
for i in range(5):
p_process = multiprocessing.Process(target=print_number,args=(i,))
p_process.start()
t2=time.time()
print("花费的时间:"+str(t2-t1))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
结果如下图


关于python中的多线程和多进程的补充说明
技术细节
1.进程的所有线程都生活在相同的内存空间中,而进程则具有各自的内存空间。
2.与进程相比,线程更轻量且开销更低。生成进程比生成线程要慢一些。
3.在线程之间共享对象更容易,因为它们共享相同的内存空间。为了在进程之间实现相同的目的,我们必须使用通常由OS提供的某种IPC(进程间通信)模型。

并行计算的陷阱(坑)
向程序引入并行性并不总是有益的;它可能会带来一定的问题。有一些陷阱要注意。最重要的如下。
1.竞争条件:正如我们已经讨论的,线程具有共享的内存空间,因此它们可以访问共享变量。当多个线程试图同时更改同一变量时,就会发生竞争状态。线程调度程序可以在线程之间任意交换,因此我们无法知道线程尝试更改数据的顺序。这可能会导致两个线程中的任何一个行为不正确,尤其是如果线程根据变量的值决定执行某些操作时。为了防止这种情况发生,可以在修改该变量的代码段周围放置互斥(或互斥)锁,以便一次只能有一个线程写入该变量。
2.饥饿:当线程长时间拒绝访问特定资源时,就会发生饥饿,结果,整个程序会变慢。这可能是设计不良的线程调度算法的意外副作用。
3.死锁:过度使用互斥锁也有一个缺点-它可能在程序中引入死锁。死锁是一种状态,当一个线程正在等待另一个线程释放锁,但是另一个线程需要资源来完成第一个线程所持有的状态。这样,两个线程都停止,程序停止。僵局可被视为饥饿的极端情况。为了避免这种情况,我们必须小心不要引入太多相互依赖的锁。
4.Livelock:Livelock是线程保持循环运行但没有任何进展的时候。这也是由于设计不良和互斥锁使用不当而引起的。

全局锁(GIL)The Global Interpreter Lock
当谈到Python时,要记住一些奇怪的地方。我们知道线程共享相同的内存空间,因此必须采取特殊的预防措施,以使两个线程不会写入同一内​​存位置。CPython解释器使用称为GIL或全局解释器锁定的机制来处理此问题。
从Python GlobalInterpreterLock Wiki:

在CPython中,全局解释器锁或GIL是互斥体,用于保护对Python对象的访问,从而防止多个线程一次执行Python字节码。锁定是必要的,主要是因为CPython的内存管理不是线程安全的。

查看此处的幻灯片,以更详细地了解Python GIL。(这个幻灯片之后也会上传到自己的资源上)
在GIL获得其完成任务,但有代价的。它在解释程序级别有效地序列化了指令。它的工作方式如下:对于要执行任何功能的任何线程,它必须获取全局锁。一次只有一个线程可以获取该锁,这意味着解释器最终将串行地运行指令。这种设计使内存管理具有线程安全性,但是结果是,它根本无法利用多个CPU内核。设计人员在开发CPython时要想到的是在单核CPU中,这并不是什么大问题。但是,如果您使用的是多核CPU,则此全局锁定最终将成为瓶颈。
但是,如果您的程序在其他地方(例如在网络,IO或用户交互中)存在更严重的瓶颈,则此瓶颈变得无关紧要。在这些情况下,线程化是一种完全有效的并行化方法。但是对于受CPU约束的程序,线程化最终会使程序变慢。

差异,优点和缺点
1.线程在相同的内存空间中运行;进程具有独立的内存。
2.从上一点出发:线程之间共享对象更容易,但另一方面,您必须采取额外措施进行对象同步,以确保两个线程不会同时写入同一对象,并且不会出现竞争条件。
3.由于增加了对象同步的编程开销,因此多线程编程更容易出错。另一方面,多进程编程很容易正确。
4.与进程相比,线程的开销较低。产生进程比线程花费更多的时间。
5.由于GIL在Python中施加的限制,线程无法利用多个CPU内核实现真正的并行性。多重处理没有任何这样的限制。
6.进程调度由OS处理,而线程调度由Python解释器完成。
7.子进程是可中断和可杀死的,而子线程则不是。您必须等待线程终止或join。
通过所有这些讨论,我们可以得出以下结论:
线程应用于涉及IO或用户交互的程序。
多处理应用于受CPU限制的计算密集型程序。

结束语
最后时刻保持了周更,嘿嘿嘿。一起加油。(能不点个赞吗?)
这个python的并发执行(多线程和多进程)自己也没想到会写这么久。正常情况下是,自己搜索某个课题,越是搜索和学习,了解的越是透彻。这次不太一样,因为网上好多关于python多线程和多进程的说法不太正确。自己也是各种查阅资料,保证了正确性,才敢写上去。如有疑问,欢迎提出。因为我也觉得身为一个博主需要对自己写出的博客的正确性做出保证。
谢谢各位看到这里,也希望大家期待这个专栏之后的文章!

最后是自己参考的资料吧,大家也可以去看看。
Multithreading VS Multiprocessing in Python:
https://medium.com/contentsquare-engineering-blog/multithreading-vs-multiprocessing-in-python-ece023ad55a
Multiprocessing Vs. Threading In Python: What You Need To Know:
https://timber.io/blog/multiprocessing-vs-multithreading-in-python-what-you-need-to-know/
what are the differences between the threading and multiprocessing-modules?
https://stackoverflow.com/questions/18114285/what-are-the-differences-between-the-threading-and-multiprocessing-modules/60451367#60451367
Multiprocessing vs. Threading in Python: What Every Data Scientist Needs to Know
https://blog.floydhub.com/multiprocessing-vs-threading-in-python-what-every-data-scientist-needs-to-know/
————————————————
版权声明:本文为CSDN博主「西瓜6」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37924224/article/details/105866342

原文地址:https://www.cnblogs.com/wangjunjiehome/p/14595718.html