多线程类似于同时执行多个不同程序,多线程运行有如下优点:
使用线程可以把占据长时间的程序中的任务放到后台去处理。
程序的运行速度可能加快
在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,可以同时进行
进程是资源分配的最小单位,一个程序至少有一个进程。
线程是程序执行的最小单位,一个进程至少有一个线程。
进程都有自己独立的地址空间,内存,数据栈等,所以进程占用资源多。由于进程的资源独立,所以通讯不方便,只能使用进程间通讯(IPC)。
线程共享进程中的数据,他们使用相同的地址空间,使用线程创建快捷,创建开销比进程小。同一进程下的线程共享全局变量、静态变量等数据,所以线程通讯非常方便,但会存在数据同步与互斥的问题,如何处理好同步与互斥是编写多线程程序的难点。
一个进程中可以存在多个线程,在单核CPU中每个进程中同时刻只能运行一个线程,只有在多核CPU中才能存在线程并发的情况。
当线程需要运行但没有运行空间时,会对线程的优先级进行判断,高优先级先运行,低优先级进程让行。
并发相关的python库
threading:基于线程的并行
multiprocessing:基于进程的并行
concurrent:并发包
concurrent.futures:启动并行任务
subprocess:子进程管理
sched:事件调度
queue:同步队列
select:等待I / O完成
dummy_threading:threading模块的替代(当_thread不可用时)
_thread:底层的线程API(threading基于其上)
_dummy_thread:_thread模块的替代(当_thread不可用时)
进程是操作系统分配程序执行资源的单位,而线程是进程的一个实体,是CPU调度和分配的单位。一个进程肯定有一个主线程,我们可以在一个进程里创建多个线程来实现多任务。
import threading
p1 = threading.Thread(target=[函数名],args=([要传入函数的参数]))
p1.start() # 启动p1线程
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
互斥锁为资源引入一个状态:锁定/非锁定
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
互斥锁有三个常用步骤:
lock = threading.Lock() # 取得锁
lock.acquire() # 上锁
lock.release() # 解锁
import threading
import time
num = 0
lock = threading.Lock() # 取得锁
def work1(loop):
global num
for i in range(loop):
# 等价于 num += 1
lock.acquire() # 上锁
temp = num
num = temp + 1
lock.release() # 解锁
print(num)
def work2(loop):
global num
for i in range(loop):
# 等价于 num += 1
lock.acquire() # 上锁
temp = num
num = temp + 1
lock.release() # 解锁
print(num)
if __name__ == '__main__':
t1 = threading.Thread(target=work1,args=(1000000,))
t2 = threading.Thread(target=work2, args=(1000000,))
t1.start()
t2.start()
while len(threading.enumerate()) != 1:
time.sleep(1)
print(num)
from queue import Queue
from threading import Thread
import time
que = Queue()
# 如果maxsize设置为小于0或者不设置,队列为无限长
#que = Queue(maxsize=2)
def task(value):
global que
que.put(value)
# que.put(value)
def task2():
global que
res = que.get()
print(res)
que.task_done()
t1 = Thread(target=task, args=(10,))
t2 = Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
que.join()
这里整理了一个使用que与threading 实现并发的Demo
def main():
start_url = Queue.Queue() # 存放url的队列
result_queue = Queue.Queue() # 结果集队列
for i in range(1, 3):
page_url = '_%s.shtml' % i
start_url.put(page_url) # 将值插入队列中
# 构建线程
thread_list = []
for n in range(4): # 创建4 个线程
t_t = threading.Thread(target=get_news_url, args=(start_url, result_queue)) # 创建线程,调用get_news_url方法,args传入参数
thread_list.append(t_t)
for t in thread_list:
t.start()
start_url.join() # 就是当所有的url全部获取完,放入到结果集里才开始存入数据库,防止出现 插入数据库报错的情况
while result_queue.qsize(): # 返回队列的大小
save_news_mysql(result_queue.get()) # 将结果存入数据库中
多线程可以实现并发 增强程序的处理能力