我相信有很多人对线程和进程只是懵懵懂懂,只知道这是个啥,知道线程和进程的区别,但就是不知道在项目中怎么用,在项目中哪里才会用到它,也包括现在写作的我,由于接触这方面的东西少,所以知道现在才对他进行学习,一边学习一边记博客,如有错误,望大佬指正,不喜勿喷。本文章简单写线程和进程的一些方法,至于具体如何在项目中运用,这个在之后的项目文章中讲吧!
区别
我记得之前看到过一篇文章举例特别好,但又有点不太合理,我自己举个例子吧!一台服务器相当于一家工厂,里面有各种流水线在工作,进程就相当于一个个的流水线,线程就相当于在流水线上工作的人,假如生产一个产品(产品就相当于一个请求),我现在又单线程和单进程,也就是一个流水线和一个人,如果产品少的话,这就没有问题,单线程和单进程足够了,但是产品需求慢慢变多了,那效率就低了,那要怎么办呢,招人呗,这时候来了一个工人(多线程),两个人在一个流水线上工作,效率增加了很多,随着产品的增多,工人越来越多,但是一个流水线上的位置(位置就相当于内存资源)只有那么多,来再多的工人也是一样,这样反而有点拥挤,那就在开一个流水线吧(多进程)。这样在合理的分配流水线和工人后工厂的效率就会大大提高(这样就做到了高并发)。
当然这只是抽象的。那个具体的例子吧!打开你的任务管理器,看到谷歌的进程。如下:
这里你没打开一个网页他就会多一个线程,然后再看看另外一个QQ:
我们的QQ有三个进程,所以线程和进程就是这么个意思。
说一点高深的:
- 进程是资源分配的基本单位,而线程是cpu执行和调度的基本单位。
- 线程内的空间资源是共享的,而进程是独立的空间,资源相互不影响,所以进程要比线程安全。
- 一个进程可以有多个线程,寄存器和栈空间是线程独有的。
- 统一线程之间可以直接通信,但是进程之间通信是需要中间代理的。
- 进程启动速度慢,线程启动速度快。
优缺点
相信看了上面后,对线程和进程也有了大概的了解。现在说下他们的优缺点。
线程:同一个进程中的线程占用的内存空间是公用的,所以他开销比较小,但是因为空间公用,所以线程之间会有影响,比如一个流水线中的一个工人做的有问题将会有可能影响其他人的工作,而且数据是共享的。
进程:每个进程之间占用的内存空间是独立的,相互不会有影响,一个流水线坏了不会影响其他流水线的工作,但是占用的资源比较大,数据也不会共享。
python多线程
对于多线程,python提供了两个库thread(python3已放弃) 和 threading ,因为我的python版本是3.5以上的所以用后面一个库。下面就开始码代码吧。
threading.
Thread
(group=None, target=None, name=None, args=(), kwargs={})
import threading, time
def thread_start(arg):
print('线程%s开始' % arg)
time.sleep(2)
print('这里是线程%s' % arg)
time.sleep(2)
print('线程%s结束' % arg)
if __name__ == "__main__":
thread1 = threading.Thread(target=thread_start, args=('1'))
thread2 = threading.Thread(target=thread_start, args=('2'))
thread1.start()
thread2.start()
'''
线程1开始
线程2开始
这里是线程2
这里是线程1
线程1结束线程2结束
'''
如果你绝的有点麻烦,那就简单封装下。代码如下:
import threading, time
class my_thread(threading.Thread):
def __init__(self, n):
super(my_thread, self).__init__()
self.n = n
def run(self):
print('线程%s开始' % self.n)
time.sleep(2)
print('这里是线程%s' % self.n)
time.sleep(2)
print('线程%s结束' % self.n)
if __name__ == "__main__":
t1 = my_thread('1')
t2 = my_thread('2')
t1.start()
t2.start()
'''
线程1开始
线程2开始
这里是线程2这里是线程1
线程2结束线程1结束
'''
得到的结果都是一样的,只不过重写了run函数。
下面是threading提供的一些方法和属性:此处参考博客
- start():创建线程后通过start启动线程,等待CPU调度,为run函数执行做准备;
- run():线程开始执行的入口函数,函数体中会调用用户编写的target函数,或者执行被重载的run函数;
- join([timeout]):阻塞挂起调用该函数的线程,直到被调用线程执行完成或超时。通常会在主线程中调用该方法,等待其他线程执行完成。
- name、getName()&setName():线程名称相关的操作;
- ident:整数类型的线程标识符,线程开始执行前(调用start之前)为None;
- isAlive()、is_alive():start函数执行之后到run函数执行完之前都为True;
- daemon、isDaemon()&setDaemon():守护线程相关;
join()
我们先看下join()的用法,官方的解释是等待当前线程,直到当前线程执行结束再继续。用一个前面的简单的例子:
import threading, time
def thread_start1(arg):
print('线程%s开始' % arg)
time.sleep(2)
print('这里是线程%s' % arg)
time.sleep(2)
print('线程%s结束' % arg)
def thread_start2(arg):
print('线程%s开始' % arg)
time.sleep(2)
print('这里是线程%s' % arg)
time.sleep(5)
print('线程%s结束' % arg)
if __name__ == "__main__":
thread1 = threading.Thread(target=thread_start1, args=('1'))
thread2 = threading.Thread(target=thread_start2, args=('2'))
thread1.start()
thread2.start()
thread1.join()
print('这里结束了所有')
'''
线程1开始线程2开始
这里是线程1这里是线程2
线程1结束
这里结束了所有
线程2结束
'''
例子中,有两个子线程thread_start1和thread_start2,两个线程同时执行,但是主线程在thread1.join()后就没有被执行,直到thread_start1线程执行完之后才把最后一个打印执行,但是thread_start2就和原来一样继续执行直到结束。如果去掉thread1.join()语句,那么结果就是:
这里结束了所有
线程1开始
线程2开始
这里是线程2
这里是线程1
线程1结束
线程2结束
主线程是不会等待子线程结束的。
daemon
然后再看下守护线程,守护线程指的是子线程绑定为主线程的守护线程,当主线程结束字后,守护线程也同样结束,成为守护线程使用的方法是setDaemon(True), 在默认情况下所有的线程都是false,只有为True的时候才会成为守护线程。该方法必须卸载start()方法之前,不然会报RuntimeError错误。
import threading, time
def thread_start1(arg):
print('线程%s开始' % arg)
time.sleep(2)
print('这里是线程%s' % arg)
time.sleep(2)
print('线程%s结束' % arg)
if __name__ == "__main__":
thread1 = threading.Thread(target=thread_start1, args=('1'))
thread1.setDaemon(True)
thread1.start()
time.sleep(3)
print('这里结束了所有')
'''
线程1开始
这里是线程1
这里结束了所有
'''-
上面结果可以看出,在线程1设置成守护线程的时候,线程没有结束的时候就随着主线程的结束而结束了。
python多进程
对于多进程python同样提供了multiprocessing模块,再次感叹python真强大。用法和线程库的用法差不多。
multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={},*, daemon=None)
创建过程也和创建线程一样既可以直接实例化,也可以创建类继承封装,举例:
import multiprocessing
import time
def my_process(n):
print('进程%s开始' % n)
time.sleep(1)
print('进程%s进行中' % n)
time.sleep(2)
print('进程%s结束' % n)
if __name__ == "__main__":
pr = multiprocessing.Process(target=my_process, args=('1',))
pr.start()
pr.join()
print('主进程结束')
'''
进程1开始
进程1进行中
进程1结束
主进程结束
'''
也可以进行封装:
from multiprocessing import Process
import time
class my_process(Process):
def __init__(self, n):
super(my_process, self).__init__()
self.n = n
def run(self):
print('进程%s开始' % self.n)
time.sleep(1)
print('进程%s进行中' % self.n)
time.sleep(2)
print('进程%s结束' % self.n)
if __name__ == "__main__":
t1 = my_process('1')
t2 = my_process('2')
t1.start()
t2.start()
t1.join()
t2.join()
print('主进程结束')
'''
进程1开始
进程2开始
进程1进行中
进程2进行中
进程1结束
进程2结束
主进程结束
'''
目前看来线程和进程的运用差不多,但是作用不同,可以测试下线程和进程分别循环一亿累加的时间计算,我用的python版本是3.7,经过我测试下来用的时间是差不多的,大概时间为4秒多,只是如果开两个进程同时计算的时间会多出来一秒的时间,原因是进程的创建和销毁会进行系统调用,会增加额外的时间。学到这只是会用,但是具体哪里用到,是不是还是一脸懵逼,我也是,哈哈,之后会继续学习,然后再项目中实践,继续更新,加油!