线程和进程创建 / 进程池(POOL),就是实现已经创建好的进程

本文详细介绍了进程与线程的概念,包括主线程、父线程和子线程之间的关系,探讨了Python中如何使用threading模块创建线程及多线程的限制,同时介绍了使用multiprocessing模块创建进程的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程池,就是实现已经创建好的进程


主线程是什么:~~~就是我们的main函数,其实main函数就是我们的主线程的入口。程序运行的时候,系统会将我们的代码加载到内存当中,进入主线程,运行代码,程序就呼啦呼啦跑起来了。

主线程,父线程,子线程的关系

这里必须先说明一点,主线程和父线程。

主线程:可以认为,主线程是程序的一条主线,一旦主线程结束,程序就结束了。其他所有线程都GAME OVER了。简单理解,主线程就是游戏中的主线任务,主线任务做完了,程序就通关,结束了。

父进程:一个进程中可以有很多的线程,CreateTread()函数中就有一个参数可以指定创建线程的入口,简单的说,就是可以自定义一个线程启动入口,这个时候可以新创建一个线程,而在这个线程中可以在此掉用CreateTread()函数来创建别的线程。这样的话,线程之间就可以层层嵌套,就和C++中的父类子类一回事,如果还无法理解,回家看C++继承。。。。~!!!

但是这里要说明一点,主线程好像不能成为任何线程的父线程,这个下面会说明的。。。。。

子线程:父线程的儿子。~!!!!!!!!、

当父线程消亡的时候,子线程是不会消亡的,是会继续执行到结束的。。。。。。。~!!!!!这个很重要。。。。。 因为线程只有在windows系统的R3层上看来是有嵌套父子关系的,而在内核当中,他们都是独立的内核对象。。。。。。  重点,都是独立的。所以,当父线程消亡的时候,子线程其实是可以继续执行的。

但是~!!当主线程消亡的时候,所有线程都得死。。。。都得死。。都得死~~!!!!!重要的事情说三遍。所以我觉得,主线程是不能看作任何线程的父线程的,这里就不满足父子线程的特性啊。。。。 

主线程
当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread),因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程。每个进程至少都有一个主线程,在Winform中,应该就是创建GUI的线程。
主线程的重要性体现在两方面:1.是产生其他子线程的线程;2.通常它必须最后完成执行比如执行各种关闭动作。


一、使用threading模块创建线程

1、三种线程创建方式

(1)传入一个函数

这种方式是最基本的,即调用threading中的Thread类的构造函数,然后指定参数target=func,再使用返回的Thread的实例调用start()方法,即开始运行该线程,该线程将执行函数func,当然,如果func需要参数,可以在Thread的构造函数中传入参数args=(...)。示例代码如下:
[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #-*-coding:utf-8-*-  
  3.   
  4. import threading  
  5. #用于线程执行的函数  
  6. def counter(n):  
  7.     cnt = 0;  
  8.     for i in xrange(n):  
  9.         for j in xrange(i):  
  10.             cnt += j;  
  11.     print cnt;              
  12.               
  13. if __name__ == '__main__':  
  14.     #初始化一个线程对象,传入函数counter,及其参数1000  
  15.     th = threading.Thread(target=counter, args=(1000,));  
  16.     #启动线程  
  17.     th.start();  
  18.     #主线程阻塞等待子线程结束  
  19.     th.join();  
这段代码很直观,counter函数是一个 很无聊的双重循环,需要注意的是 th.join()这句,这句的意思是主线程将自我阻塞,然后等待th表示的线程执行完毕再结束,如果没有这句,运行代码会立即结束。join的意思比较晦涩,其实将这句理解成这样会好理解些“while th.is_alive(): time.sleep(1)”。虽然意思相同,但是后面将看到,使用join也有陷阱。

(2)传入一个可调用的对象

许多的python 对象都是我们所说的可调用的,即是任何能通过函数操作符“()”来调用的对象(见《python核心编程》第14章) 类的对象也是可以调用的,当被调用时会自动调用对象的内建方法__call__(),因此这种新建线程的方法就是给线程指定一个__call__方法被重载了的对象。示例代码如下:
[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #-*-coding:utf-8-*-  
  3.   
  4. import threading  
  5. #可调用的类  
  6. class Callable(object):  
  7.     def __init__(self, func, args):  
  8.         self.func = func;  
  9.         self.args = args;  
  10.     def __call__(self):  
  11.         apply(self.func, self.args);  
  12.   
  13. #用于线程执行的函数  
  14. def counter(n):  
  15.     cnt = 0;  
  16.     for i in xrange(n):  
  17.         for j in xrange(i):  
  18.             cnt += j;  
  19.     print cnt;   
  20.   
  21. if __name__ == '__main__':  
  22.     #初始化一个线程对象,传入可调用的Callable对象,并用函数counter及其参数1000初始化这个对象  
  23.     th = threading.Thread(target=Callable(counter, (1000,)));  
  24.     #启动线程  
  25.     th.start();  
  26.     #主线程阻塞等待子线程结束  
  27.     th.join();  
这个例子关键的一句是apply(self.func, self.args); 这里使用初始化时传入的函数对象及其参数来进行一次调用。

(3)继承Thread类

这种方式通过继承Thread类,并重载其run方法,来实现自定义的线程行为,示例代码如下:
[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #-*-coding:utf-8-*-  
  3.   
  4. import threading, time, random  
  5.   
  6. def counter():  
  7.     cnt = 0;  
  8.     for i in xrange(10000):  
  9.         for j in xrange(i):  
  10.             cnt += j;  
  11.   
  12. class SubThread(threading.Thread):  
  13.     def __init__(self, name):  
  14.         threading.Thread.__init__(self, name=name);  
  15.   
  16.     def run(self):  
  17.         i = 0;  
  18.         while i < 4:  
  19.             print self.name,'counting...\n';  
  20.             counter();  
  21.             print self.name,'finish\n';  
  22.             i += 1;  
  23.   
  24. if __name__ == '__main__':  
  25.   
  26.     th = SubThread('thread-1');  
  27.     th.start();  
  28.     th.join();  
  29.     print 'all done';  
这个例子定义了一个SubThread类,它继承了Thread类,并重载了run方法,在方法中调用counter4次并打印一些信息,可以看到这种方式比较直观。在构造函数中要记得先调用父类的构造函数进行初始化。

2、python多线程的限制

python多线程有个讨厌的限制,全局解释器锁(global interpreter lock),这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不是“并行”。手册上的解释是为了保证对象模型的正确性!这个锁造成的困扰是如果有一个计算密集型的线程占着cpu,其他的线程都得等着....,试想你的多个线程中有这么一个线程,得多悲剧,多线程生生被搞成串行;当然这个模块也不是毫无用处,手册上又说了:当用于IO密集型任务时,IO期间线程会释放解释器,这样别的线程就有机会使用解释器了!所以是否使用这个模块需要考虑面对的任务类型。

二、使用multiprocessing创建进程

1、三种创建方式

进程的创建方式跟线程完全一致,只不过要将threading.Thread换成multiprocessing.Process。multiprocessing模块尽力保持了与threading模块在方法名上的一致性,示例代码可参考上面线程部分的。

在Python中创建进程有两种方式,第一种是:

[python] view plain copy

  1. from multiprocessing import Process  
  2. import time  
  3.   
  4. def test():  
  5.     while True:  
  6.         print('---test---')  
  7.         time.sleep(1)  
  8.   
  9. if __name__ == '__main__':  
  10.     p=Process(target=test)  
  11.     p.start()  
  12.     while True:  
  13.         print('---main---')  
  14.         time.sleep(1)  
上面这段代码是在windows下跑的,通过Process类可以创建一个进程对象,然后p.start()即可开启进程,test函数是你想进程实现的功能。

第二种方式是:

通过子类继承Process类,子类中必须有run方法,里面实现进程功能,创建子类对象之后,调用对象的start方法。


进程池,就是实现已经创建好的进程

  1. from multiprocessing import Process  
  2. import time  
  3.   
  4. class MyNewProcess(Process):  
  5.     def run(self):  
  6.         while True:  
  7.             print('---1---')  
  8.             time.sleep(1)  
  9.   
  10. if __name__=='__mian__':  
  11.     p = MyNewProcess()  
  12.     # 调用p.start()方法,p会先去父类中寻找start(),然后在Process的start方法中调用run方法  
  13.     p.start()  
  14.   
  15.     while True:  
  16.         print('---Main---')  
  17.         time.sleep(1)  
第三种方式:

Pool方法就是创建了一个进程池,3表示创建进程数,通过pool.apply_async( )将子进程添加到进程池中,pool.close( )表示关闭进程池,一定要添加join函数,否则主进程直接断了,不能看到子进程的结果。

  1. # 进程池  
  2. from multiprocessing import Pool  
  3. import os  
  4. import random  
  5. import time  
  6.   
  7. def worker(num):  
  8.     for i in range(5):  
  9.         print('===pid=%d==num=%d='%(os.getpid(),num))  
  10.         time.sleep(1)  
  11.   
  12. # 3表示进程池中最多有三个进程一起执行  
  13. pool=Pool(3)  
  14.   
  15. for i in range(10):  
  16.     print('---%d---'%i)  
  17.     # 向进程中添加任务  
  18.     # 注意:如果添加的任务数量超过了进程池中进程的个数的话,那么就不会接着往进程池中添加,如果还没有执行的话,他会等待前面的进程结束,然后在往  
  19.     # 进程池中添加新进程  
  20.     pool.apply_async(worker,(i,))  
  21.   
  22. pool.close() # 关闭进程池  
  23. pool.join()  # 主进程在这里等待,只有子进程全部结束之后,在会开启主线程  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值