python高级编程——线程和线程池
线程模块
线程的特点:
本质上是异步的、需要多个并发活动、每个活动的处理顺序可能是不确定的、或者说是随机的,不可预测的,宏观上是同时运行的
进程与线程的关系:
多任务可以由多进程完成,也可以由一个进程内的多线程完成,进程有若干个线程组成,一个进程至少有一个线程。在使用线程的过程中一般建议使用threading模块,相比于_thread高级一些。很多地方线程和进程是一样的
threading模块的Thread类:
属性: name ------>名字
ident ------->线程标识符
daemon ------->守护线程的标识,类型bool
方法:__init__构造函数,和进程的构造函数差不多,可以参考进程的构造函数
start:线程启动
run:定义线程功能方法,一般是在子类重新定义的
join:在启动线程终止前一直挂起,timeout是阻塞时间
使用Thread类创建线程的三种方法:(直接看实例)
①创建Thread类实例,传给他一个函数
# 1、创建Thread类实例,传给它一个函数 # 线程的属性和方法 def task(task_id, task_time): print("start task", task_id, "at", ctime()) sleep(task_time) print("task", task_id, "done at", ctime()) if __name__ == "__main__": print("准备创建线程") # 创建Thread类实例,传给它一个函数,传参也是关键字参数 t = threading.Thread(target=task, args=(1, 2)) print("准备启动线程") # 两种设置守护线程的方式, # 守护线程和守护进程就是主进程运行完,守护进(线)程立刻结束 # t.setDaemon(True) t.daemon = True t.start() # 名字以Thread-N起名,N从1开始 print("线程的名字:", t.name) print("线程的id:", t.ident) print("线程已经启动")
②创建Thread的实例,传给他一个可调用的实例化对象
# 2、创建Thread的实例,传给他一个可调用的类的实例化对象 # 重写方法__call__ def task(task_id, task_time): print("start task", task_id, "at", ctime()) sleep(task_time) print("task", task_id, "done at", ctime()) # 创建一个简单的类 class ThreadFunc(object): def __init__(self, *args): super().__init__() self.args = args # __call__功能是使得实例化对象也可以调用 def __call__(self, *args, **kwargs): task(*self.args) if __name__ == "__main__": t = threading.Thread(target=ThreadFunc(1, 2)) t.start() t.join()
③派生Thread的子类,并创建子类的实例(推荐)
# 3、派生Thread的子类,并创建子类的实例(推荐) def task(task_id, task_time): print("start task", task_id, "at", ctime()) sleep(task_time) print("task", task_id, "done at", ctime()) class MyThread(threading.Thread): def __init__(self, *args): super().__init__() self.args = args def run(self): task(*self.args) if __name__ == "__main__": myThread = MyThread(1, 2) # 这里不是调用run方法,和进程的使用差别不大 myThread.start() myThread.join()
线程的状态(一般是简化之后的)
1. 新建(NEW):新创建了一个线程对象。
2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用 了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调 度选中,获取cpu 的使用权 。
3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片( timeslice) ,执行程序代码。
4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也 即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状 态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况 分三种:
(一). 等待阻塞:sleep
(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线 程占用。
5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run() 方法,则该线程结束生命周期。死亡的线程不可再次复生。
状态图切换:
总结:在一个进程的多个线程是可以共享进程的全局变量的,但是多个线程若同时修改这个全局变量,就可能造成多个线程之间对全局变量的混乱(即线程是不安全的)下一篇就是就要讲到锁机制了。