计算机,用于计算的机器。计算机的核心是CPU,在现在多核心的电脑很常见了。为了充分利用cpu核心做计算任务,程序实现了多线程模型。通过多线程实现多任务的并行执行。
现在的操作系统多是多任务操作系统。每个应用程序都有一个自己的进程。操作系统会为这些进程分配一些执行资源,例如内存空间等。在进程中,又可以创建一些线程,他们共享这些内存空间,并由操作系统调用,以便并行计算。
创建线程之后,线程并不是始终保持一个状态。其状态大概如下:
New
创建。Runnable
就绪。等待调度Running
运行。Blocked
阻塞。阻塞可能在 Wait
Locked
Sleeping
Dead
消亡这些状态之间是可以相互转换的,一图胜千颜色:
threading_state
(图片引用 内心求法博客)
线程中执行到阻塞,可能有3种情况:
- 同步:线程中获取同步锁,但是资源已经被其他线程锁定时,进入Locked状态,直到该资源可获取(获取的顺序由Lock队列控制)
- 睡眠:线程运行sleep()或join()方法后,线程进入Sleeping状态。区别在于sleep等待固定的时间,而join是等待子线程执行完。当然join也可以指定一个“超时时间”。从语义上来说,如果两个线程a,b, 在a中调用b.join(),相当于合并(join)成一个线程。最常见的情况是在主线程中join所有的子线程。
- 等待:线程中执行wait()方法后,线程进入Waiting状态,等待其他线程的通知(notify)。
线程有着不同的状态,也有不同的类型。大致可分为:
相比进程,线程更加轻量,可以实现并发。可是在python的世界里,对于线程,就不得不说一句GIL(全局解释器锁)。GIL的存在让python的多线程多少有点鸡肋了。Cpython的线程是操作系统原生的线程在解释器解释执行任何Python代码时,都需要先获得这把锁才行,在遇到 I/O 操作时会释放这把锁。因为python的进程做为一个整体,解释器进程内只有一个线程在执行,其它的线程都处于等待状态等着GIL的释放。
关于GIL可以有更多的趣事,一时半会都说不完。总之python想用多线程并发,效果可能还不如单线程(线程切换耗时间)。想要利用多核,可以考虑使用多进程。
虽然python线程比较鸡肋,可是也并发一无是处。多了解还是有理由对并发模型的理解。
Python提供两个模块进行多线程的操作,分别是thread
和threading
,前者是比较低级的模块,用于更底层的操作,一般应有级别的开发不常用。后者则封装了更多高级的接口,类似java的多线程风格,提供run
方法和start
调用。
12345678910111213141516171819202122 | import timeimport threading class MyThread(threading.Thread): def run(self): for i in range(5): print \’thread {}, @number: {}\’.format(self.name, i) time.sleep(1) def main(): print \”Start main threading\” # 创建三个线程 threads = [MyThread() for i in range(3)] # 启动三个线程 for t in threads: t.start() print \”End Main threading\” if __name__ == \’__main__\’: main() |
输入如下:(不同的环境不一样)
1234567891011121314 | Start main threadingthread Thread–1, @number: 0thread Thread–2, @number: 0thread Thread–3, @number: 0End Main threadingthread Thread–1, @number: 1thread Thread–3, @number: 1thread Thread–2, @number: 1thread Thread–3, @number: 2thread Thread–1, @number: 2 thread Thread–2, @number: 2thread Thread–2, @number: 3thread Thread–1, @number: 3thread Thread–3, @number: 3 |
每个线程都依次打印 0 – 3 三个数字,可是从输出的结果观察,线程并不是顺序的执行,而是三个线程之间相互交替执行。此外,我们的主线程执行结束,将会打印 End Main threading
。从输出结果可以知道,主线程结束后,新建的线程还在运行。
上述的例子中,主线程结束了,子线程还在运行。如果需要主线程等待子线程执行完毕再退出,可是使用线程的join
方法。join方法官网文档大概是
join(timeout)
方法将会等待直到线程结束。这将阻塞正在调用的线程,直到被调用join()方法的线程结束。
主线程或者某个函数如果创建了子线程,只要调用了子线程的join方法,那么主线程就会被子线程所阻塞,直到子线程执行完毕再轮到主线程执行。其结果就是所有子线程执行完毕,才打印 End Main threading
。只需要修改上面的main
函数
12345678910111213 | def main(): print \”Start main threading\” threads = [MyThread() for i in range(3)] for t in threads: t.start() # 一次让新创建的线程执行 join for t in threads: t.join() print \”End Main threading\” |
输入如下:
12345678910 | Start main threading过多线程实现多任务的并行执行。
现在的操作系统多是多任务操作系统。每个应用程序都有一个自己的进程。操作系统会为这些进程分配一些执行资源,例如内存空间等。在进程中,又可以创建一些线程,他们共享这些内存空间,并由操作系统调用,以便并行计算。 线程状态创建线程之后,线程并不是始终保持一个状态。其状态大概如下:
这些状态之间是可以相互转换的,一图胜千颜色: threading_state (图片引用 内心求法博客) 线程中执行到阻塞,可能有3种情况:
线程类型线程有着不同的状态,也有不同的类型。大致可分为:
Python线程与GIL相比进程,线程更加轻量,可以实现并发。可是在python的世界里,对于线程,就不得不说一句GIL(全局解释器锁)。GIL的存在让python的多线程多少有点鸡肋了。Cpython的线程是操作系统原生的线程在解释器解释执行任何Python代码时,都需要先获得这把锁才行,在遇到 I/O 操作时会释放这把锁。因为python的进程做为一个整体,解释器进程内只有一个线程在执行,其它的线程都处于等待状态等着GIL的释放。 关于GIL可以有更多的趣事,一时半会都说不完。总之python想用多线程并发,效果可能还不如单线程(线程切换耗时间)。想要利用多核,可以考虑使用多进程。 线程的创建虽然python线程比较鸡肋,可是也并发一无是处。多了解还是有理由对并发模型的理解。 Python提供两个模块进行多线程的操作,分别是
输入如下:(不同的环境不一样)
每个线程都依次打印 0 – 3 三个数字,可是从输出的结果观察,线程并不是顺序的执行,而是三个线程之间相互交替执行。此外,我们的主线程执行结束,将会打印 线程合并(join方法)上述的例子中,主线程结束了,子线程还在运行。如果需要主线程等待子线程执行完毕再退出,可是使用线程的
主线程或者某个函数如果创建了子线程,只要调用了子线程的join方法,那么主线程就会被子线程所阻塞,直到子线程执行完毕再轮到主线程执行。其结果就是所有子线程执行完毕,才打印
输入如下:
|