2018年8月25日多线程编程总结

今天遇到的新单词:
grid   n格子,方格,坐标
stick  n插入

PYTHON 本身也支持多任务处理,并且提供了如下的操作方式
多线程多任务处理机制   (比较常用)
多进程多任务处理机制   (不常用,大型项目开发或者系统开发会用)
协程多任务处理机制       (不常用)

线程等于微进程,协程等于微线程

多线程总结:
python中提供了两个模块支持多线程,分别是thread和threading,但是thread模块过于底层,对于新手
来说并不是特别的友好,要求多线程的程序开发逻辑思维清晰同时又具备大量开发经验的
情况下,可以控制的非常精细,PYTHON3 中将 thread 模块进行了规范内置,更名为_thread,友好的提醒如果你不是并发编
程的骨灰级爱好者,请不要轻易尝试使用_thread 进行操作,而是推荐使用操作更加灵活使
用更加简洁的 threading 模块进行并发编程的处理,下面详细讲解threading模块:

多线程编程需要引入threading模块:
import threading

threading 模块的属性和方法
名称             描述
Thread            线程类,用于创建和管理线程
Event              事件类,用于线程同步
Condition        条件类,用于线程同步
Lock/RLock     锁类,用于线程同步
Timer              延时线程,用于在一定事件后执行一个函数
Semaphore/BoundedSemaphore  信号量类,用于线程同步
active_count()/activeCount()  获取当前 alive 状态的所有线程数量
current_thread()/currentThread()  获取当期正在执行的线程对象
get_ident()         获取运行中程序当前线程的唯一编号
enumerate()      获取所有 alive 状态线程列表
local                  线程局部数据类
stack_size([size])  获取线程占用内存栈的大小
main_thread()   获取主线程

用法展示:
#获取当前存活的线程的总数,该数量包括并发的线程和主线程的总数
t = threading.activeCount()   
print(t)

# 获取当前线程对象【线程名称】
print(threading.current_thread())
print(threading.current_thread().getName())
print(threading.get_ident())

# 获取主线程对象
print(threading.main_thread())
print(threading.main_thread().getName())

Thread 类型的属性和方法
名称             描述
__init__(name,target,args,kwargs)  构造方法,创建线程类型
is_alive()/isAlive()   判断当前线程是否 alive 状态
run()                  线程执行方法,面向对象自定义线程必须重写该函数
start()                线程启动方法
join([timeout=None])   线程独占,等待当前线程运行结束或者超时才能执行其他线程
ident                  标识当前线程的唯一编号
name                 当前线程名称
daemon             布尔值,判断当前线程是否守护线程
(_thread模块默认是守护的
  threading模块默认是不守护的
  multiprocessing默认是不守护的)

#创建一个线程:
t1 = threading.Thread(name="线程1", target=sing)
创建线程的时候一定要事先创建一个函数,用于线程的target.

#判断指定的线程是否存活,返回True是存活,返回False是死亡
print(t1.is_alive())

#独占属性:让指定的线程独占CPU时间片,知道当前线程运行结束/超时
t1.join()

#获取指定线程的编号
t1.ident

#启动线程
t1.start()

#守护属性,thrading模块中默认是不守护的,不过可以修改守护属性的值。
t1.daemon = True
这时就将线程1的守护属性设为了守护,当主线程运行完之后,无论t1线程时是否
运行结束t1都会跟着运行结束

#获取指定线程的名字
t1.name

#创建多个线程:
for i in range(5):
    t = threading.Thread(name="线程"+str(i), target=sing)
    t.start()
#获取当前线程的名字
print(threading.current_thread().getName())

面向过程编程:

加锁解锁:案例火车票售票问题:
锁分为互斥锁(Lock())和可重用锁(RLock())
lock1 = threading.Lock()
lock2 = threading.Rlock()
面向过程的多线程编程需要加锁来解决多线程数据的共享问题,但是需要注意死锁的出现
lock = threading.Lock()先创建一个锁对象
lock.acquire()加锁
lock.release()解锁
死锁问题的案例是哲学家吃饭问题:
因为哲学家A和哲学家B是两个线程,对各自的数据重复上锁,可能会导致死锁的问题存在,让程序无法
继续向下运行,处于一种永久等待的状态。

为了解决线程之间的数据访问和线程之间的通信问题,python提供了多线程同步事件对象和多线程同步条件对象,
常用的是条件对象,不过python也提供队列的数据类型,安全访问数据不会造成线程的冲突。所以队列更常用。
1.事件对象,案例卖油条买油条:
事件对象不需要加锁来解决数据的共享问题,但是需要添加标记,清除标记和等待事件
event = threading.Event()   #创建一个事件对象
在编程过程中需要用到让对方线程等待或者唤醒的事件
等待是:event.wait()
打标记就相当于唤醒对方的wait,打标记的方式是:
event.set()
event.clear()
打完标记之后需要立即清除,如果不清除的话该线程下面的等待语句不会执行,整个的流程语句
会一下子执行完,set打完标记之后本线程的语句会执行到wait语句,对方线程的语句可以执行wait
语句之后的语句

2.条件对象,案例生产者和消费者问题:
多线程同步条件对象需要加锁和等待唤醒来解决数据的共享问题,加锁的方式和普通的多线程编程方式不同:
con = threading.Condition()   #创建一个条件对象
con.acquire()加锁
con.release()解锁
同时线程有等待和唤醒两种操作:
线程的等待:con.wait()   /  con.wait_for()
线程的唤醒:con.notify() /  con.notify_all()

多线程之间的数据共享:
多线程并发编程的重点是解决线程之间访问共享数据和线程之间通信的问题
为了解决线程之间数据共享问题,PYTHON 提供了一个数据类型【队列】可以用于在多线程
并发模式下,安全的访问数据而不会造成数据共享冲突,队列是由queue模块提供的Queue类型创建:
它的特性是当一个线程正在访问队列时,队列会自动拒绝其他线程的访问
队列主要有两个方法:
put(data, [timeout=None])  向队列中添加数据,队列如果满了,一直阻塞直到超时或者队    
列中有数据被删除之后添加成功
get([timeout=None])  从队列中获取数据,如果队列为空,一直阻塞直到超时或者队
列中添加数据之后获取成功

basket = queue.Queue(10)  #生成一个队列

向队列中添加数据:
no = random.randint(0, 10)
basket.put(no, timeout=1)
从队列中取数据:
no2 = basket.get(timeout=1)

当遇到比较大项目的多线程并发编程时,可以使用面向对象的多线程编程
面向对象多线程编程的基本语法:                                         
面向对象创建多线程时,不需要用t1=threading.Thread(name,target,args),而是自定义一个类,继承threading.Thread类型,
先初始化一下函数,继承自父类,然后重写Thread类型的run方法,在run方法中填写想要执行的程序,写完之后
实例化自定义类的对象就直接可以产生一个线程,然后对象名.start()可以直接启动线程,start方法会自动启动run方法,
不需要专门去调用run方法。
面向对象多线程编程中需要定义锁对象,然后对数据进行加锁和解锁操作
lock = threading.Lock()
con.acquire()加锁
con.release()解锁

猜你喜欢

转载自blog.csdn.net/qq_40994972/article/details/82049877