(1)python下多线程的限制以及多进程中传递参数的方式
python多线程有个全局解释器锁(global interpreter lock),这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不是“并行”。
多进程间共享数据,可以使用 multiprocessing.Value 和 multiprocessing.Array
(2)python多线程与多进程的区别
-
-
在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以,有必要对每个Process对象调用join()方法 (实际上等同于wait)。对于多线程来说,由于只有一个进程,所以不存在此必要性。
-
多进程应该避免共享资源。在多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以上方法并不合适。此时我们可以通过共享内存和Manager的方法来共享资源。但这样做提高了程序的复杂度,并因为同步的需要而降低了程序的效率。
进程与线程的关系:
1. 线程是最小的调度单位
2. 进程是最小的管理单元
3. 一个进程必须至少一个线程
4. 没有线程,进程也就不复存在
线程特点:
3 线程的并发是利用cpu上下文的切换(是并发,不是并行)
4 多线程执行的顺序是无序的
5 多线程共享全局变量
6 线程是继承在进程里的,没有进程就没有线程
7 GIL全局解释器锁
8 只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就
9 很合适
线程详解:
import threading # ---》导入模块 # def func(n): # print('task',n) # t = threading.Thread(target = func,args = (1,)) # t.start() # # # for i in range(10): ---》使t的内容循坏输出10行 # t = threading.Thread(target = func,args = ('t-%s'%i,)) ---》target=函数名 args要求以元组形式传参,当参数只有一个时,以(参数,)的格式传参。 # t.start() ----》固定启动线程 # 多线程共享全局变量 # g=0 ---》设置一个全局变量 # def test(): # global g ----》线程共享全局变量时需要建立在声明global+全局变量上 # for i in range(10): # g +=1 # print(g) # def test1(): # global g # for i in range(10): # g +=1 # print(g) # t1=threading.Thread(target=test) ---->用线程调用test函数的结果,没有参数时,只需要输入target就行 # t2=threading.Thread(target=test1) # t1.start() # t2.start()
协程:
资源的自动切换,耗资源小,效率高。
GIL全局解释器锁:
#GIL全局解释器锁 ---》作用是保证最多只有一个线程在使用全局变量g_num,但是不保证赋值成功 lock = threading.Lock() ----》添加互斥锁,作用是使两个线程不再并发处理,使赋值成功,注意L要大写 g_num = 0 def test1(): global g_num lock.acquire() ---》在循坏前锁上 for i in range(100000): g_num +=1 lock.release() ----》赋值结束后解放锁 def test2(): global g_num lock.acquire() for i in range(100000): g_num +=1 lock.release() t1=threading.Thread(target=test1) ---》注意T要大写 t2=threading.Thread(target=test2) t1.start() # 导致结果不对的原因(1):主线程处理py文件速度比另外两个线程赋值速度快,在没赋值结束时就又开始新一轮的赋值 t2.start() # 解决方法(1): # 在后面加上两行 t1.join() #----》即主线程等待每轮赋值结束后才重启下一轮赋值,但是这样也还是不对,只能让成功次数上升 t2.join() # 因为赋值成功率不是100%,次数越多越有可能赋值失败。 print(g_num) 解决方法(2): 添加互斥锁:lock = threading.Lock() ,保证赋值成功,缺点是速度慢
进程详解:
# 一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的 # 进程之间是相互独立的,结果互不影响也无法共享全局变量,等待上一个进程结束,下一个进程再执行。 # cpu密集的时候适合用多进程 #cpu效率高,耗资源多, import multiprocessing --》导入进程模块 import time ----》导入快捷键,将鼠标停在关键词上alt 加 enter 选择导入模块 def test1(n): time.sleep(2) ---》两秒后打印执行任务完毕,再到第二个进程 print('task',n) def test2(n): time.sleep(2) print('task',n) if __name__ == '__main__': --->处理py文件,这是主进程的作用 p1 = multiprocessing.Process(target=test1,args=(1,)) --》注意P的大写 p2 = multiprocessing.Process(target=test2,args=(1,)) p1.start() ---》此进程负责调用p1进程 p2.start() ---》负责调用p2进程
进程池:
import multiprocessing import time #进程池的并发必须导入和引用时间模块 g_num = 0 def test1(n): for i in range(n): time.sleep(1) print('test1', i) def test2(n): for i in range(n): time.sleep(1) print('test2', i) def test3(n): for i in range(n): time.sleep(1) print('test3', i) def test4(n): for i in range(n): time.sleep(1) print('test4', i) if __name__ == '__main__': #此操作必须存在,不然无法调用 pool = multiprocessing.Pool(3) #把进程声明出来括号里不写东西说明无限制,如果写数字,就是最大的进程数,即允许几个进程并发。池外的串行 pool.apply_async(test1,(5,)) #用pool去调用函数test1,参数为5格式为(5,) pool.apply_async(test2,(5,)) pool.apply_async(test3,(5,)) pool.apply_async(test4,(5,)) #以上三个进程并发处理,同时输出赋值,而4进程串行输出,因为在进程池外** pool.close()
pool.join() #close必须在join的前面