进程和线程
Posted imxiaoqi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程和线程相关的知识,希望对你有一定的参考价值。
单核心的CPU和多核心的CPU实现多任务的基本方法。
即使多核心的CPU真正实现了并行多任务的工作,但是任务的数量远多于核心数,因此,多任务一般是将多个任务轮流分配到每个核心上执行。
实现多任务的方法可以从几个方面着手:
多进程、多线程、协程、多进程+多线程
并行和并发的概念(提纲)
线程(threading)
单线程处理
import time
def eat():
print('吃饭')
time.sleep(5)
print('吃饭结束')
def whtch():
print('看手机')
time.sleep(5)
print('看手机结束')
# 使用单线程
def main():
eat()
whtch()
if __name__ == '__main__':
print(time.time())
main()
print(time.time())
多线程处理
import threading
import time
def eat():
print('吃饭')
time.sleep(5)
print('吃饭结束')
def whtch():
print('看手机')
time.sleep(5)
print('看手机结束')
def main():
# 创建一个子线程对象
e = threading.Thread(target=eat)
# 启动子线程
e.start()
# 又创建一个子线程对象
w = threading.Thread(target=whtch)
w.start()
if __name__ == '__main__':
print(time.time())
main()
print(time.time())
查看程序中的所有线程数量
import threading
import time
def eat():
print('吃饭')
time.sleep(2)
print('吃饭结束')
def whtch():
print('看手机')
time.sleep(5)
print('看手机结束')
def main():
e = threading.Thread(target=eat)
e.start()
w = threading.Thread(target=whtch)
w.start()
if __name__ == '__main__':
main()
while True:
# 查看所有的线程
# 我们可以看到,主线程是最后结束的
print(threading.enumerate())
time.sleep(1)
#if len(threading.enumerate()) <= 1:
# break
使用自定义类进行多线程处理
通过前面的代码,我们可以看到,如果想打开一个子线程,只需要给线程执行一个处理函数,然后启动这个线程即可。但是我们能否使用类进行多线程的编写呢?当然是可以的。
import threading
import time
# 我们自定义的类必须要继承threading.Thread
class XXX(threading.Thread):
# 这里必须要定义一个run方法
def run(self):
print(threading.enumerate())
# self.name中保存的是当前线程的名字
print(self.name)
time.sleep(2)
if __name__ == '__main__':
x1 = XXX()
x1.start()
x2 = XXX()
x2.start()
关于Python中全局变量的使用
在python的函数中,针对全局变量的使用:
如果使用的时候,需要使全局变量的指向发生变化,那么在使用的时候必须在全局变量前加上global
如果使用的时候,仅仅是更改了全局变量指向的值,指向并没有发生变化的话,使用的时候不需要在全局变量前加global
a = 1
def g1():
# global a
a = 10
g1()
# 在函数内部,如果不使用global,那么就不会使用全局变量a
print(a)
b = [20, 30]
def g2():
b[0] = 111
g2()
print(b)
在各个线程中,全局变量可以共享
import threading
import time
num = 0
def fun1():
global num
for i in range(0, 500):
num += 1
def fun2():
global num
for i in range(0, 500):
num += 1
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
time.sleep(1)
print(num)
创建进程对象的时候,为要执行的函数传递参数
我们知道,对于单个cpu来说,其某一个时间点,只能执行一个任务,我们之所以看着好像可以执行多任务,其实是因为操作系统将我们的各个任务分成了很多小任务,这些小任务交替执行,我们看着好像是同时执行而已。
那么,我们可以想到,我们每个线程执行的是一个函数,这些函数里面有很多语句,如果语句并不是完整的分到一个小任务中,那么共享的东西可能会被覆盖赋值,因此会造成数据不准确。
import threading
import time
def eat(sec):
print('吃饭')
time.sleep(sec)
print('吃饭结束')
def whtch(sec, other):
print('看手机')
time.sleep(sec)
print(other)
print('看手机结束')
if __name__ == '__main__':
t1 = threading.Thread(target=eat, args=(1,))
t2 = threading.Thread(target=whtch, args=(5, '和同学聊天'))
t1.start()
t2.start()
全局变量共享带了的问题
import threading
import time
num = 0
def fun1():
global num
for i in range(0, 500000):
num += 1
print('fun1:%s' % num)
def fun2():
global num
for i in range(0, 500000):
num += 1
print('fun2:%s' % num)
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
time.sleep(3)
print(num)
互斥锁
引入锁的概念后,对于任何一个资源来说,其可以有两个状态:锁定和非锁定。
当某个线程要更改一个数据的时候,可以先将该数据锁定,此时该数据处于锁定状态,其它的线程不能更改它,直到该线程操作完该数据,将其解锁,然后该数据就处于了非锁定状态,其它资源此时才可以使用该资源。互斥锁可以保证每次都只有一个线程执行写入操作。
import threading
import time
num = 0
# 创建一个锁
lock = threading.Lock()
def fun1():
global num
# 上锁
lock.acquire()
for i in range(0, 500000):
num += 1
# 解锁
lock.release()
print('fun1:%s' % num)
def fun2():
global num
# 上锁
lock.acquire()
for i in range(0, 500000):
num += 1
# 解锁
lock.release()
print('fun2:%s' % num)
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
time.sleep(3)
print(num)
被锁的资源应该越少越好
import threading
import time
num = 0
# 创建一个锁
lock = threading.Lock()
def fun1():
global num
for i in range(0, 500000):
# 上锁
lock.acquire()
num += 1
# 解锁
lock.release()
print('fun1:%s' % num)
def fun2():
global num
for i in range(0, 500000):
# 上锁
lock.acquire()
num += 1
# 解锁
lock.release()
print('fun2:%s' % num)
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
time.sleep(3)
print(num)
死锁
在多线程操作共享资源的时候,如果两个线程分别占有一部分资源并且同时等待对方释放资源,那么此时就造成了死锁。(一般出现在多个锁的时候)
import threading
import time
# 创建锁
lock1 = threading.Lock()
lock2 = threading.Lock()
def fun1():
lock1.acquire()
print('func1 开始执行')
time.sleep(1)
lock2.acquire()
print('func1 执行结束')
lock2.release()
lock1.release()
def fun2():
lock2.acquire()
print('func2 开始执行')
time.sleep(1)
lock1.acquire()
print('func2 执行结束')
lock1.release()
lock2.release()
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
进程(multiprocessing)
进程:正在运行着的程序,每个进程都有自己的独立的内存区域,每个内存区域内都有自己的堆、栈、数据段、代码段,因此全局变量值在各子进程中不能共享。
import multiprocessing
import time
def eat(sec):
print('吃饭')
time.sleep(sec)
print('吃饭结束')
def whtch(sec, other):
print('看手机')
time.sleep(sec)
print(other)
print('看手机结束')
if __name__ == '__main__':
# 创建进程对象
p1 = multiprocessing.Process(target=eat, args=(2,))
p2 = multiprocessing.Process(target=whtch, args=(5, '吹一波'))
# 启动进程
p1.start()
p2.start()
我们可以看到,在Python中使用进程和使用线程,使用方式十分的相似
队列(Queue)
from multiprocessing import Queue
# 获取一个最大只能存5个数据的队列对象
q1 = Queue(5)
# 检验队列是否满了
print(q1.empty())
# 向队列中放数据
q1.put('大哥')
q1.put((1, 2, 3))
q1.put(10000)
q1.put(['帅哥', '美女'])
q1.put({'name': '张思睿'})
# 向队列中放数据,并且不希望等待
q1.put_nowait({'name': '张三'})
# 检验队列是否满
print(q1.full())
# 从队列中取数据
print(q1.get())
print(q1.get())
print(q1.get())
print(q1.get())
print(q1.get())
# 取数据,并且不希望等待
print(q1.get_nowait())
进程池
使用进程池可以重复使用进程池里进程。一般用户进程数量不定或者进程数量较大的时候。
在初始化进程池的时候,可以指定该池能存放的进程的最大数量,当有新任务需要使用进程的时候,如果该进程池没有满,那么会创建一个新的进程去执行该任务,如果进程池已满,那么该任务会等待着,等该进程池中有进程的任务结束的时候,会用该进程来执行新的任务。
from multiprocessing import Pool
import os, random,time
# 定义一个工作函数
def worker(xxx, yyy):
print(xxx, random.randint(0, 100))
# os.getpid()可以获取当前进程的id, os.getppid()可以获取当前进程的父进程id
print(yyy, '进程id:%s' % os.getpid())
time.sleep(1)
if __name__ == '__main__':
# 定义一个最大只能存放5个进程的进程池
p1 = Pool(5)
for i in range(0, 20):
# 调用工作函数,第一个参数表示进程执行的函数,第二个参数表示进程执行函数需要的参数
p1.apply_async(worker, ('哈哈哈', '呵呵呵'))
print('开始')
# 进程池必须先调用close(),才能调用join()
p1.close()
# 如果一个进程调用了join,表示该进程执行完以后才会执行主进程
p1.join()
守护进程
守护进程会随着主进程的代码的执行完毕而结束。
def eat(sec):
print('吃饭')
time.sleep(sec)
print('吃饭结束')
if __name__ == '__main__':
p1 = multiprocessing.Process(target=eat, args=(2,))
# 设置该子进程为守护进程
p1.daemon = True
p1.start()
其它方法
# 判断子进程是否活着
p.is_alive()
# 结束子进程
p.terminate()
# 获取进程的名字
p.name
以上是关于进程和线程的主要内容,如果未能解决你的问题,请参考以下文章