多线程和多进程
- 程序:一大堆代码以文本的形式存入一个文档
- 进程:程序运行的一个状态
- 包含地址空间、内存、数据栈等
- 每个进程由自己完全独立的运行环境,多进程共享数据是一个问题
- 线程
- 一个进程的独立运行片段,一个进程可以有多个线程
- 轻量化的进程
- 一个进程的多个线程间共享数据和上下文运行环境
- 共享互斥问题
- 全局解释器锁(GIL)
- Python代码的执行是由python虚拟机进行控制
- 在主循环中只能有一个控制线程在执行
- Python包
-
thread:有问题,不好用,python3改成了_thread
- 调用thread模块中的start_new_thread()函数来产生新线程
thread.start_new_thread ( function, args[, kwargs] )
- 参数说明:
- function - 线程函数
- args - 传递给线程函数的参数,他必须是个tuple类型
- kwargs - 可选参数
-
import _thread
import time
# 为线程定义一个函数
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
# 创建两个线程
try:
_thread.start_new_thread( print_time, ("Thread-1", 2, ) )
_thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print ("Error: 无法启动线程")
while 1:# 让主线程遗址等待
pass
# 输出
Thread-1: Wed Jul 24 15:17:17 2019
Thread-2: Wed Jul 24 15:17:19 2019Thread-1: Wed Jul 24 15:17:19 2019
Thread-1: Wed Jul 24 15:17:21 2019
Thread-2: Wed Jul 24 15:17:23 2019
Thread-1: Wed Jul 24 15:17:23 2019
Thread-1: Wed Jul 24 15:17:25 2019
Thread-2: Wed Jul 24 15:17:27 2019
Thread-2: Wed Jul 24 15:17:31 2019
Thread-2: Wed Jul 24 15:17:35 2019
- threading:通行的包
- 直接使用threading.Thread
- t = threading.Thread(target=xxx, args=(xxx,))
- t.start():启动线程
- t.join():等待多线程执行完成
- 直接使用threading.Thread
#方式一
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' %name)
if __name__ == '__main__':
t=Thread(target=sayhi,args=('egon',))
t.start()
print('主线程')
- 直接继承threading.Thread
- 直接继承Thread
- 重写run函数
- 类实例可以直接运行
#方式二
from threading import Thread
import time
class Sayhi(Thread):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
time.sleep(2)
print('%s say hello' % self.name)
if __name__ == '__main__':
t = Sayhi('egon')
t.start()
print('主线程')
守护线程deamon
- 无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
- 一般认为,守护线程不允许离开主线程独立运行
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' %name)
if __name__ == '__main__':
t=Thread(target=sayhi,args=('egon',))
t.setDaemon(True) #必须在t.start()之前设置
t.start()
print('主线程')
print(t.is_alive())
-
线程常用属性
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
-
共享变量
- 当多个线程同时访问一个变量的时候,会产生共享变量问题
-
解决方法(锁,信号灯)
- 锁:
- 是一个标志,表示一个线程在占用一些资源
- 使用方法
- 上锁
- 使用共享变量
- 释放锁
- 锁:
import threading
sum = 0
loopSum = 10000
lock = threading.Lock()
def myAdd():
global sum, loopSum
for i in range(1, loopSum):
# 上锁
lock.acquire()
sum += 1
# 释放锁
lock.release()
def myMinu():
global sum, loopSum
for i in range(1, 10000):
lock.acquire()
sun -= 1
lock.release()
if __name__ == '__main__':
print("Starting .......{0}".format(sum))
t1 = threading.Thread(target=myAdd, args=())
t2 = threading.Thread(target=myMinu(), args=())
t1.start()
t2.start()
t1.join()
t2.join()
print("Done .......{0}".format(sum))
- 线程安全问题
- 如果一个变量,对于多线程来讲,不加锁也不会引起任何问题,则称为线程安全
- 线程不安全变量类型:list,set,dict
- 线程安全变量类型:queue
- 生产者消费者问题
- 一个模型
- 可以用来搭建消息队列
- queue是一个用来存放变量的数据结构,特点是先进先出,内部元素排队
import queue
import threading
import time
class Producer(threading.Thread):
def run(self):
global queue
count = 0
while True:
if queue.qsize()<1000:
for i in range(100):
count = count + 1
msg = '生成产品' + str(count)
queue.put(msg)
print(msg)
time.sleep(0.5)
class Consumer(threading.Thread):
def run(self):
global queue
while True:
if queue.qsize() > 100:
for i in range(3):
msg = self.name + '消费了' + queue.get()
print(msg)
time.sleep(1)
if __name__ == '__main__':
queue = queue.Queue()
for i in range(500):
queue.put('初始产品' + str(i))
for i in range(2):
p = Producer()
p.start()
for i in range(5):
c = Consumer()
c.start()
- 死锁问题
import threading
import time
lock_1 = threading.Lock()
lock_2 = threading.Lock()
def func_1():
print("func_1 staring..........")
lock_1.acquire()
print("func_1 申请了lock_1.....")
time.sleep(2)
print("fun_1 等待 lock_2.......")
lock_2.acquire()
print("fun_1 申请了 lock_2.....")
lock_2.release()
print("func_1 释放了 lock_2......")
lock_1.release()
print("func_1 释放了 lock_1")
print("func_1 done.........")
def func_2():
print("func_2 staring..........")
lock_2.acquire()
print("func_2 申请了lock_2.....")
time.sleep(4)
print("fun_2 等待 lock_1.......")
lock_1.acquire()
print("fun_2 申请了 lock_1.....")
lock_1.release()
print("func_2 释放了 lock_1......")
lock_2.release()
print("func_2 释放了 lock_2")
print("func_2 done.........")
if __name__ == '__main__':
print("主程序启动。。。。。。。。")
t1 = threading.Thread(target=func_1(),args=())
t2 = threading.Thread(target=func_2(), args=())
t1.start()
t2.start()
t1.join()
t2.join()
print("主程序启动。。。。。。。。。。。。。。。")
- 锁的等待时间问题
- lock_1.acquire(timeout=4)
- semphore
- 允许一个资源最多由一个线程同时访问
import threading
import time
# 参数表示最多由几个线程同时使用
semphore = threading.Semaphore(3)
def func():
if semphore.acquire():
for i in range(5):
print(threading.currentThread().getName() + 'get semphore')
time.sleep(15)
semphore.release()
print(threading.currentThread().getName() + 'realease semphore')
for i in range(8):
t1 = threading.Thread(target=func())
t1.start()
- threading.Timer
- 利用多线程,在指定时间后启动一个功能
import threading
import time
def func():
print("I am running.........")
time.sleep(4)
print("I am done .........")
if __name__ == '__main__':
t = threading.Timer(6, func)
t.start()
i = 0
while True:
print("{0}*********************".format(i))
time.sleep(3)
i += 1
- 可重入锁
- 一个锁,可以被线程多次申请
- 主要解决递归调用的时候,需要申请锁的情况
import threading
import time
num = 0
mutex = threading.RLock()
class MyThread(threading.Thread):
def run(self):
global num
time.sleep(1)
if mutex.acquire(1):
num = num + 1
msg = self.name + 'set num to ' + str(num)
print(msg)
mutex.acquire()
mutex.release()
mutex.release()
def ta():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
ta()
线程替代方案
- subprocess
- 完全跳过线程,使用进程
- 是派生进程的主要替代方案
- python2.4后引入
- multiprocessing
- 使用threading接口派生,使用子进程
- 允许为多核或者多cpu派生进程,接口跟threading非常相似
- python2.6
- concurrent.futures
- 新的异步执行模块
- 任务级别的操作
- python3.2后引入
多进程
- 进程间通讯(InterprocessCommunication,IPC)
- 进程之间没有任何共享状态
- 进程的创建
- 直接生成Process实例对象
- 派生子类
import multiprocessing
from time import sleep, ctime
def clock(interval):
while True:
print("the time is %s" % ctime())
sleep(interval)
if __name__ == '__main__':
p = multiprocessing.Process(target=clock, args=(5, ))
p.start()
while True:
print("sleeping......")
sleep(1)
import multiprocessing
from time import sleep, ctime
class ClockProcess(multiprocessing.Process):
# 两个比较重要的函数init、run
def __init__(self, interval):
super().__init__()
self.interval = interval
def run(self):
while True:
print("the time is %s" % ctime())
sleep(self.interval)
if __name__ == '__main__':
p = ClockProcess(3)
p.start()
while True:
print("sleeping..........")
sleep(1)
- 在os中查看pid,ppid以及他们的关系
from multiprocessing import Process
import os
def info(title):
print(title)
print('module name', __name__)
# 得到父进程id
print('parent process:', os.getppid())
print('process id:', os.getpid())
def f(name):
info('function f')
print('hello', name)
if __name__ == '__main__':
info('main line')
p = Process(target=f, args=('bob',))
p.start()
p.join()
- 生产者消费者模型
- JoinableQueue
import multiprocessing
from time import ctime
def consumer(input_q):
print("Info consumer:", ctime())
while True:
# 处理项
item = input_q.get()
print("pull", item, "out of q")# 此处替换为有用的工作
input_q.task_done() # 发出信号通知任务完成
print("out of consumer:", ctime()) # 此句未执行
def producer(sequence, output_q):
print("Into proceder:", ctime())
for item in sequence:
output_q.put(item)
print("put", item, "into q")
print("out of procuder:", ctime())
if __name__ == '__main__':
q = multiprocessing.JoinableQueue()
# 运行消费者进程
cons_p = multiprocessing.Process(target=consumer, args=(q,))
cons_p.daemon = True
cons_p.start()
# 生产多个项,sequeue代表要发送给消费者的序列项
# 在实践中,这可能是生成器的输出或通过一些其他方式生产出来
sequeue = [1,2,3,4]
producer(sequeue. q)
q.join()
- 哨兵
import multiprocessing
from time import ctime
# 设置哨兵问题
def consumer(input_q):
print("Info consumer:", ctime())
while True:
# 处理项
item = input_q.get()
if item is None:
break
print("pull", item, "out of q")# 此处替换为有用的工作
print("out of consumer:", ctime()) # 此句未执行
def producer(sequence, output_q):
print("Into proceder:", ctime())
for item in sequence:
output_q.put(item)
print("put", item, "into q")
print("out of procuder:", ctime())
if __name__ == '__main__':
q = multiprocessing.Queue()
# 运行消费者进程
cons_p = multiprocessing.Process(target=consumer, args=(q,))
cons_p.start()
# 生产多个项,sequeue代表要发送给消费者的序列项
# 在实践中,这可能是生成器的输出或通过一些其他方式生产出来
sequeue = [1,2,3,4]
producer(sequeue. q)
q.put(None)
cons_p.join()