1,一个cpu一次只能执行一个任务, 多个cpu同时可以执行多个任务
2,一个cpu一次只能执行一个进程, 其他进程处于非运行状态
3,进程里包含的执行单元叫线程,一个进程可以包含多个线程
4,一个进程的内存空间是共享的,每个进程里的线程都可以使用这个共享空间
5,一个线程在使用这个共享空间的时候, 其它的线程必须等待(阻塞状态)
6,互斥锁作用就是防止多个线程同时使用这块内存空间, 先使用的线程会将空间上锁, 其它的线程处于等待状态, 等锁开了才能进
7,进程: 表示程序的一次执行
8,线程:CPU运算的基本调度单位
9,GIL(全局锁):Python里的执行通行证, 而且只有一个, 拿到通行证的线程就可以进入CPU执行任务, 没有GIL的线程就不能执行任务
10, Python的多线程适用于大量密集的I/O处理
11,Python的多进程适用于大量的密集并行计算
添加线程
threading.Thread(target, args)
使用threading.Thread()新建一个线程, target是需要执行的函数, args是需要传入该函数的参数, args接受一个tuple, 即使只有一个参数 也需要写成(x,)形式
import threading
def thread_job():
print('1111')
def main():
thread = threading.Thread(target=thread_job,) # 添加一个线程
thread.start() # 开始该线程
if __name__ == '__main__':
main()
线程阻塞:join
join()的作用是调用该线程时, 等待该线程完成之后在继续往下执行
join通常用于主线程与子线程之间, 主线程等待子线程运行完毕后在继续执行, 避免子程序和主程序同时执行, 子程序还没有运行完的时候就已经运行结束
import threading
import time
def T1_job():
print('T1 start\n')
for i in range(10):
time.sleep(10)
print('T1 finish\n')
def T2_job():
print('T2 start\n')
print('T2 finish\n')
def main():
thread1 = threading.Thread(target=T1_job, name='T1')
thread2 = threading.Thread(target=T2_job, name='T2')
thread1.start()
thread1.join() # 等待执行thread1完成后才能进行下一步-主程序
thread2.start()
thread2.join()
print('all done')
if __name__ == '__main__':
main()
Queue(队列对象)
Queue是Python中的标准库, 可以直接import Queue引用, 队列是线程间最常用的交换数据的形式
Python下多线程的思考
对于资源, 加锁是个重要的环节, 因为Python原生的list ,dict等, 都是 not thread safe的, 而Queue,是线程安全的, 因此在满足使用条件下, 建议使用队列
1, 初始化: class.Queue.Queue(maxsize) FIFO先进先出
2, 包中国的常用方法:
Queue.qsize()返回队列的大小
Queue.empty()如果队列为空, 返回True,反之False
Queue.full()如果队列满了, 返回True,反之False
Queue.full与maxsize大小对应
Queue.get([block[, timeout]])获取队列, timeout等待时间
3, 创建一个"队列"对象
import Queue
myqueue = Queue.Queue(maxsize=10)
4,将一个值放入队列中
myqueue.put(10)
5,将一个值从队列中取出
myqueue.get()
import threading
from queue import Queue
"""
# Queue是python标准库中的线程安全的队列(FIFO)实现, 提供了一个适用于多线程编程的先进先出的数据结构,即队列.
# Queue是一种先进先出的数据结构, 一般来说读数据都从Queue头读, 写数据都从Queue尾写入
# """
def job(l,q):
for i in range(len(l)):
l[i] = l[i] ** 2
q.put(l) # 线程中, return获取的值无法提取, 需要放入q中
def multithreading():
q = Queue() # 队列
threads = [] # 全部线程
data = [[1, 2, 3], [3, 4, 5], [4, 4, 4], [5, 5, 5]]
for i in range(4):
t = threading.Thread(target=job, args=(data[i], q)) # 4个线程来执行job函数
t.start()
threads.append(t) # 当前线程加入全部线程中
# 对主线程的每一个线程都执行join()
for thread in threads:
thread.join()
results = [] # 保存结果
for _ in range(4):
results.append(q.get()) # 从q中取值, 每次只能按顺序拿出一个值
print(results)
if __name__ == '__main__':
multithreading()
"""
线程锁:Lock
lock在不同线程使用同一共享内存时, 能够确保线程之间互不影响
使用lock的方法是: 在每个线程执行运算修改共享内存之前执行lock.acquire()将共享内存上锁, 确保当前线程执行时, 内存不会被其他线程访问; 执行运算完毕后使用lock.release() 将锁关闭, 保证其他的线程可以使用该共享内存. lock.acquire()和lock.release()必须成对出现
下面代码 将lock.acquire(), lock.release()注释掉后, 输出结果是混乱的, 加锁后, 输出结果按顺序执行
# """
def job1():
global A, lock
lock.acquire() # 打开锁
for i in range(10):
A += 1
time.sleep(2)
print('job1', A)
lock.release() # 关闭锁
def job2():
global A, lock
lock.acquire() # 打开锁
for i in range(10):
A += 10
time.sleep(2)
print('job2', A)
lock.release() # 关闭锁
if __name__ == '__main__':
lock = threading.Lock() # lock锁
A = 0
t1 = threading.Thread(target=job1)
t2 = threading.Thread(target=job2)
t1.start()
t2.start()
import threading
from multiprocessing.dummy import Pool as ThreadPool
"""
线程池有几种方法可以实现, 这里我们使用multiprocessing.dummy库
"""
def job(i):
print(i, '\n', threading.current_thread())
if __name__ == '__main__':
pool = ThreadPool(4) # 创建一个包含4个线程的线程池
pool.map(job, range(12))
pool.close() # 关闭线程池的写入
pool.join() # 阻塞, 保证子线程运行完毕后在继续主进程