线程与进程的基本概念的介绍
进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。我们自己在python文件中写了一些代码,这叫做程序,运行这个python文件的时候,这叫做进程。
线程
线程(Thread)是操作系统能够进行运算调度的最小单位,他被包含在进程之中,是进程中的实际运作单位
多线程的基本概念
多线程是指在软件或硬件上实现多个线程并发执行的技术。具有多线程的计算机因有硬件支持而能够在同一时间执行多个线程。软件多线程是说即便处理器只能运行一个线程,操作系统也可以快速地在不同线程间切换,由于时间间隔小,给用户造成一种多个线程在同时运行的假象,这样的运行机制被称为软件多线程。
全局解释器锁
全局解释器锁(Global Interpreter Lock)是计算机程序设计语言解释器用于同步线程的工具,使得在同一进程内任何时刻仅有一个线程在执行。常见例子有CPython(JPython不使用GIL)与Ruby MRI。(主要用于解决CPython的内存管理不是线程安全的)。
Python线程模块
Python标准库中关于线程的主要是_thread和threading模块
_thread模块
标准库中的_thread模块作为低级别的模块存在,一般不建议直接使用,所以不做介绍。
threading模块
threading模块不仅提供了面向对象的线程实现方式,还提供了各种有用的对象和方法方便我们创建和控制线程,使用threading模块创建线程很方便,大部分操作都是围绕threading.Thread类来实现的。
接下来举一个简单地例子
import threading
import time
def run(name):
print(name,"线程执行了")
time.sleep(5)
#程序执行时,本身就是一个线程,叫主线程
#手动创建的是子线程
#主线程的执行中不会等待子线程执行完毕就会直接执行后面的代码
# 创建线程
t1 = threading.Thread(target=run, args=("t1",))
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t2.start()
t1.join()#设置子线程执行完毕再去执行主线程
t2.join()
print("执行完毕")
结果:
E:\anewproject\venv\Scripts\python.exe E:/anewproject/haha.py
A窗口 卖出一张票,还剩 99 张
B窗口 卖出一张票,还剩 98 张
A窗口 卖出一张票,还剩 97 张
B窗口 卖出一张票,还剩 96 张
A窗口 卖出一张票,还剩 95 张
B窗口 卖出一张票,还剩 94 张
A窗口 卖出一张票,还剩 93 张
B窗口 卖出一张票,还剩 92 张
A窗口 卖出一张票,还剩 91 张
B窗口 卖出一张票,还剩 90 张
A窗口 卖出一张票,还剩 89 张
B窗口 卖出一张票,还剩 88 张
A窗口 卖出一张票,还剩 87 张
B窗口 卖出一张票,还剩 86 张
A窗口 卖出一张票,还剩 85 张
B窗口 卖出一张票,还剩 84 张
A窗口 卖出一张票,还剩 83 张
B窗口 卖出一张票,还剩 82 张
A窗口 卖出一张票,还剩 81 张
B窗口 卖出一张票,还剩 80 张
A窗口 卖出一张票,还剩 79 张
B窗口 卖出一张票,还剩 78 张
A窗口 卖出一张票,还剩 77 张
B窗口 卖出一张票,还剩 76 张
A窗口 卖出一张票,还剩 75 张
B窗口 卖出一张票,还剩 74 张
A窗口 卖出一张票,还剩 73 张
B窗口 卖出一张票,还剩 72 张
A窗口 卖出一张票,还剩 71 张
B窗口 卖出一张票,还剩 70 张
A窗口 卖出一张票,还剩 69 张
B窗口 卖出一张票,还剩 68 张
A窗口 卖出一张票,还剩 67 张
B窗口 卖出一张票,还剩 66 张
A窗口 卖出一张票,还剩 65 张
B窗口 卖出一张票,还剩 64 张
A窗口 卖出一张票,还剩 63 张
B窗口 卖出一张票,还剩 62 张
A窗口 卖出一张票,还剩 61 张
B窗口 卖出一张票,还剩 60 张
A窗口 卖出一张票,还剩 59 张
B窗口 卖出一张票,还剩 58 张
A窗口 卖出一张票,还剩 57 张
B窗口 卖出一张票,还剩 56 张
A窗口 卖出一张票,还剩 55 张
B窗口 卖出一张票,还剩 54 张
A窗口 卖出一张票,还剩 53 张
B窗口 卖出一张票,还剩 52 张
A窗口 卖出一张票,还剩 51 张
B窗口 卖出一张票,还剩 50 张
A窗口 卖出一张票,还剩 49 张
B窗口 卖出一张票,还剩 48 张
A窗口 卖出一张票,还剩 47 张
B窗口 卖出一张票,还剩 46 张
A窗口 卖出一张票,还剩 45 张
B窗口 卖出一张票,还剩 44 张
A窗口 卖出一张票,还剩 43 张
B窗口 卖出一张票,还剩 42 张
A窗口 卖出一张票,还剩 41 张
B窗口 卖出一张票,还剩 40 张
A窗口 卖出一张票,还剩 39 张
B窗口 卖出一张票,还剩 38 张
A窗口 卖出一张票,还剩 37 张
B窗口 卖出一张票,还剩 36 张
A窗口 卖出一张票,还剩 35 张
B窗口 卖出一张票,还剩 34 张
A窗口 卖出一张票,还剩 33 张
B窗口 卖出一张票,还剩 32 张
A窗口 卖出一张票,还剩 31 张
B窗口 卖出一张票,还剩 30 张
A窗口 卖出一张票,还剩 29 张
B窗口 卖出一张票,还剩 28 张
A窗口 卖出一张票,还剩 27 张
B窗口 卖出一张票,还剩 26 张
A窗口 卖出一张票,还剩 25 张
B窗口 卖出一张票,还剩 24 张
A窗口 卖出一张票,还剩 23 张
B窗口 卖出一张票,还剩 22 张
A窗口 卖出一张票,还剩 21 张
B窗口 卖出一张票,还剩 20 张
A窗口 卖出一张票,还剩 19 张
B窗口 卖出一张票,还剩 18 张
A窗口 卖出一张票,还剩 17 张
B窗口 卖出一张票,还剩 16 张
A窗口 卖出一张票,还剩 15 张
B窗口 卖出一张票,还剩 14 张
A窗口 卖出一张票,还剩 13 张
B窗口 卖出一张票,还剩 12 张
A窗口 卖出一张票,还剩 11 张
B窗口 卖出一张票,还剩 10 张
A窗口 卖出一张票,还剩 9 张
B窗口 卖出一张票,还剩 8 张
A窗口 卖出一张票,还剩 7 张
B窗口 卖出一张票,还剩 6 张
A窗口 卖出一张票,还剩 5 张
B窗口 卖出一张票,还剩 4 张
A窗口 卖出一张票,还剩 3 张
B窗口 卖出一张票,还剩 2 张
A窗口 卖出一张票,还剩 1 张
B窗口 卖出一张票,还剩 0 张
票已经卖完
Process finished with exit code 0
接下来举一个复杂一些的例子:
import time
import datetime
import threading
def get_time_str():
now = datetime.datetime.now()
return datetime.datetime.strftime(now,"%H:%M:%S")
def thread_function(thread_id):
print("THread %d\t start at %s"%(thread_id,get_time_str()))
print("Thread %d\t sleeping"%thread_id)
time.sleep(4)
print("Thread %d\t finish at %s"%(thread_id,get_time_str()))
def main():
print("Main thread start at %s"%(get_time_str()))
threads = []
#创建线程
for i in range(5):
thread = threading.Thread(target=thread_function,args=(i,))
threads.append(thread)
#启动线程
for i in range(5):
threads[i].start()
time.sleep(1)
#等待线程执行完毕
for i in range(5):
threads[i].join()#子线程执行完毕之后再执行主线程
print("Main thread finish at %s"% get_time_str())
if __name__ == "__main__":
main()
结果:
E:\anewproject\venv\Scripts\python.exe E:/anewproject/hahahaha.py
Main thread start at 18:39:12
THread 0 start at 18:39:12
Thread 0 sleeping
THread 1 start at 18:39:13
Thread 1 sleeping
THread 2 start at 18:39:14
Thread 2 sleeping
THread 3 start at 18:39:15
Thread 3 sleeping
Thread 0 finish at 18:39:16
THread 4 start at 18:39:16
Thread 4 sleeping
Thread 1 finish at 18:39:17
Thread 2 finish at 18:39:18
Thread 3 finish at 18:39:19
Thread 4 finish at 18:39:20
Main thread finish at 18:39:20
Process finished with exit code 0
还有一种常见的方法就是我们可以从threading.Thread派生一个子类,在这个子类中调用父类的构造函数并实现run方法即可
import time
import datetime
import threading
def get_time_str():
now = datetime.datetime.now()
return datetime.datetime.strftime(now,"%H:%M:%S")
class MyThread(threading.Thread):
#重写父类的构造函数
def __init__(self,thread_id):
super(MyThread,self).__init__()
self.thread_id = thread_id
def run(self):
print("Thread %d\t start at %s"%(self.thread_id,get_time_str()))
print("THread %d\t sleeping"%self.thread_id)
time.sleep(4)
print("Thread %d\t finish at %s"%(self.thread_id,get_time_str()))
def main():
print("Main thread start at %s"%get_time_str())
threads = []
#创建线程
for i in range(5):
thread = MyThread(i)
threads.append(thread)
#启动线程
for i in range(5):
threads[i].start()
time.sleep(1)
#等待线程执行完毕
for i in range(5):
threads[i].join()
print("Main thread finish at %s"%get_time_str())
if __name__ == "__main__":
main()
结果:
E:\anewproject\venv\Scripts\python.exe E:/anewproject/hahahaha.py
Main thread start at 19:16:21
Thread 0 start at 19:16:21
THread 0 sleeping
Thread 1 start at 19:16:22
THread 1 sleeping
Thread 2 start at 19:16:23
THread 2 sleeping
Thread 3 start at 19:16:24
THread 3 sleeping
Thread 0 finish at 19:16:25
Thread 4 start at 19:16:25
THread 4 sleeping
Thread 1 finish at 19:16:26
Thread 2 finish at 19:16:27
Thread 3 finish at 19:16:28
Thread 4 finish at 19:16:29
Main thread finish at 19:16:29
Process finished with exit code 0
start方法会创立线程,调用一些内部启动方法之后再调用我们实现的run方法
线程同步
多线程的优势在于可以同时运行多个任务,但当线程需要处理同一个资源时,就要考虑数据不同步的问题了,如下所示:
import time
import threading
thread_lock = None
class MyThread(threading.Thread):
#重写父类的构造函数
def __init__(self,thread_id):
super(MyThread,self).__init__()
self.thread_id = thread_id
def run(self):
#锁定
thread_lock.acquire()
for i in range(3):
print("Thread %d\t printing!times:%d" %(self.thread_id,i))
thread_lock.release()
time.sleep(1)
thread_lock.acquire()
for i in range(3):
print("Thread %d\t printing!times:%d" % (self.thread_id,i))
thread_lock.release()
def main():
print("Main thread start")
threads = []
#创建线程
for i in range(5):
thread = MyThread(i)
threads.append(thread)
#启动线程
for i in range(5):
threads[i].start()
#等待线程执行完毕
for i in range(5):
threads[i].join()
print("Main thread finish at %s")
if __name__ == "__main__":
thread_lock = threading.Lock();
main()
结果:
E:\anewproject\venv\Scripts\python.exe E:/anewproject/hahahaha.py
Main thread start
Thread 0 printing!times:0
Thread 0 printing!times:1
Thread 0 printing!times:2
Thread 1 printing!times:0
Thread 1 printing!times:1
Thread 1 printing!times:2
Thread 2 printing!times:0
Thread 2 printing!times:1
Thread 2 printing!times:2
Thread 3 printing!times:0
Thread 3 printing!times:1
Thread 3 printing!times:2
Thread 4 printing!times:0
Thread 4 printing!times:1
Thread 4 printing!times:2
Thread 1 printing!times:0
Thread 1 printing!times:1
Thread 1 printing!times:2
Thread 0 printing!times:0
Thread 0 printing!times:1
Thread 0 printing!times:2
Thread 4 printing!times:0
Thread 4 printing!times:1
Thread 4 printing!times:2
Thread 3 printing!times:0
Thread 3 printing!times:1
Thread 3 printing!times:2
Thread 2 printing!times:0
Thread 2 printing!times:1
Thread 2 printing!times:2
Main thread finish at %s
Process finished with exit code 0
队列
使用共享变量来实现线程之间传递信息和数据需要我们手动控制锁,标椎库提供了一个非常有用的Queue模块,可以帮我们自动控制锁,保证数据同步,Queue类实现了一个基本的先进先出(FIFO)容器,使用put将元素添加到序列尾端,使用get从队列尾端移除元素
Queue模块不一定要使用多线程,例子如下:
from queue import Queue
q = Queue()
for i in range(5):
q.put(i)
for i in range(5):
print(q.get())
结果:
E:\anewproject\venv\Scripts\python.exe E:/anewproject/python.py
0
1
2
3
4
Process finished with exit code 0
多线程同步如下:
import time
import threading
import queue
#创建工作队列并且限制队列的最大元素个数是十个
work_queue = queue.Queue(maxsize=10)
#创建结果队列并且限制队列的最大元素个数是十个
result_queue = queue.Queue(maxsize=10)
class WorkerThread(threading.Thread):
def __init__(self,thread_id):
super(WorkerThread,self).__init__()
self.thread_id = thread_id
def run(self):
while not work_queue.empty():
#从工作队列里获取数据
work = work_queue.get()
#模拟工作3秒
time.sleep(3)
out = "Thread %d\t received %s"%(self.thread_id,work)
#把结果放入结果队列
result_queue.put(out)
def main():
#工作队列放入数据
for i in range(10):
work_queue.put("message id %d"%i)
#开启两个工作线程
for i in range(2):
thread = WorkerThread(i)
thread.start()
#输出10个结果
for i in range(10):
result = result_queue.get()
print(result)
if __name__ == "__main__":
main()
结果:
E:\anewproject\venv\Scripts\python.exe E:/anewproject/python.py
Thread 0 received message id 0
Thread 1 received message id 1
Thread 1 received message id 3
Thread 0 received message id 2
Thread 0 received message id 5
Thread 1 received message id 4
Thread 1 received message id 7
Thread 0 received message id 6
Thread 0 received message id 9
Thread 1 received message id 8
Process finished with exit code 0
进程
from multiprocessing import Process
import time
def run(name):
print(name,"进程执行了")
time.sleep(5)
#在Windows系统中创建和执行线程必须要写在if __name__ == '__main__':中
if __name__ == '__main__':
# 创建线程
p1 = Process(target=run, args=("p1",))
p2 = Process(target=run, args=("p2",))
p3 = Process(target=run, args=("p3",))
p4 = Process(target=run, args=("p4",))
p5 = Process(target=run, args=("p5",))
p1.start()
p2.start()
p3.start()
p4.start()
p5.start()
#多进程可以真正实现多个任务并行
结果:
E:\anewproject\venv\Scripts\python.exe E:/anewproject/dui.py
p1 进程执行了
p2 进程执行了
p3 进程执行了
p4 进程执行了
p5 进程执行了