Twenty-eight 线程

 

1、线程理论:

  a、线程是进程中的一员,同一进程之间的几个线程共享一个进程的资源

  b、线程可以之间被CPU调度,因此线程是计算机中能被CPU调度的最小单位

  c、为什么要有线程:开启和销毁速度比进程快的多,线程之间切换效率更高

  d、全局解释器锁 GIL:保证一个进程之间的多个线程只能有一个线程被CPU调度

2、进程和线程的区别:

  数据隔离与数据共享

3、threading 模块(thread模块升级版):线程模块

# 创建子线程
import os
import time
from threading import Thread
def func():
    time.sleep(1)    # 为了能看到主线程运行后的效果
    print('我是子线程',os.getpid())
for i in range(5):
    t = Thread(target=func)
    t.start()
print('我是主线程',os.getpid())
# 查看线程id
import time
from threading import Thread,currentThread,enumerate,active_count
def func(i):
time.sleep(1)
print('我是子线程%s'% i,currentThread().ident)
for i in range(10):
t = Thread(target=func,args = (i,))
t.start()
print(enumerate()) # 线程id等信息(包括主线程)
print(active_count()) # 个数(包括主线程)
# 开启线程的第二种方式
import os
from threading import Thread
class MyThread(Thread):
    def run(self):
        print('子线程',self.ident,os.getpid())
for  i in range(10):
    t = MyThread()
    t.start()

4、守护线程

import time
from threading import Thread
def main():
    print('main start')
    time.sleep(3)
    print('main done')

def daemon_func():
    while True:
        time.sleep(1)
        print('守护线程')

def thread_son():
    print('son start')
    time.sleep(5)
    print('son done')

t = Thread(target=daemon_func)
t.setDaemon(True)
t.start()
Thread(target=thread_son).start()
main()
# 守护线程和守护进程不同 会守护主线程直到主线程结束,如果主线程要等待其他子线程,那么守护线程在这段时间中仍然发挥守护作用

5、线程锁:GIL是从一定程度上保证了数据的安全,但是如果当一个线程取值进行修改时刚好遇上时间片结束没有及时把结果放回去,就可能出现值错误

import time
from threading import Thread,Lock # 互斥锁
n = 0
def func(lock):
    global n
    with lock:
        n += 1

lock = Lock()
t_lst = []
for i in range(100):
    t = Thread(target=func,args=(lock,))
    t.start()
    t_lst.append(t)
for t in t_lst:
    t.join()
print(n)
# 1.没有多个线程操作同一个变量的时候 就可以不加锁
# 2.如果你是执行基础数据类型的内置方法 :lst.append,lst.pop,lst.extand,lst.remove,dic.get['key']都是线程安全的
# dis模块 可以把你的python代码翻译成cpu指令
# 先取一个值 再做某个操作 再把这个结果存储回去
# 科学家吃面问题:多个锁被多个线程抢的时候要运行需要多个锁的代码时就会阻塞
# 方法一:改成一个锁
import time
from threading import Lock,Thread,RLock
lock = Lock()
def eat1(name):
    lock.acquire()
    print('%s拿到面条了'%name)
    print('%s拿到叉子了'%name)
    print('%s开始吃面'%name)
    time.sleep(0.2)
    print('%s将叉子放回'%name)
    print('%s将面条放回'%name)
    lock.release()
def eat2(name):
    lock.acquire()
    print('%s拿到叉子了' % name)
    print('%s拿到面条了' % name)
    print('%s开始吃面' % name)
    time.sleep(0.2)
    print('%s将面条放回' % name)
    print('%s将叉子放回' % name)
    lock.release()

Thread(target=eat1,args=('alex',)).start()
Thread(target=eat2,args=('wusir',)).start()
Thread(target=eat1,args=('yuan',)).start()
Thread(target=eat2,args=('taibai',)).start()

# 方法二,递归锁
from threading import Lock,RLock
lock = RLock()
lock.acquire()
print(1)
lock.acquire()
print(2)
lock.release()
lock.release()
# 什么是死锁现象,使用了多把锁在一个线程内进行了多次Acquire导致了不可恢复的阻塞
# 思索现象是怎么形成的? # 两个锁 锁了两个资源 我要做某件事 需要同时拿到这两个资源 多个线程同时执行这个步骤
# 什么是递归锁(rlock)、互斥锁(lock):递归锁是不容易发生死锁现象的,互斥锁的使用不当容易发生死锁
# 递归锁可以快速的帮我们解决死锁问题
# 死锁的真正问题不在于互斥锁,而在于对于互斥锁的混乱使用,要想真正的解决死锁问题,还是要找出互斥锁的问题进行修正才能解决根本

6、线程队列/栈是后进先出/堆是值高的先出

# 为什么要有队列?
# 1.多个线程之间维持一个数据先后的秩序
# 2.线程模块的队列是线程之间数据安全的
import queue
# 先进先出的队列 :维护用户的请求顺序
q = queue.Queue(3)
# q.get()     # 在队列为空时会阻塞
q.put(1)     # 在队列满时会阻塞

# 解决方法
try:
    print(q.get_nowait())  # 在队列为空的时候也不阻塞,这时候会抛异常queue.Empty
except queue.Empty:
    pass
try:
    q.put_nowait(1)   
except queue.Full:
    pass

# 其它方法:不够准确,因为一个队列有瞬间的变化
q.qsize()  # 当前队列中有多少个值
q.empty()  # 当前队列是否为空
q.full()   # 当前队列是否为满

7、线程池:控制一定的数量/以下线程跟进程池的创建很相似,对以后代码的修改有一定的好处,跟pool一样

# 创建线程池
import time
from threading import currentThread
from concurrent.futures import ThreadPoolExecutor  # 非常新的模块,线程池,提供异步调用
def func(i):
    time.sleep(1)
    print('子线程%s'%i,currentThread().ident)

tp = ThreadPoolExecutor(5)
for i in range(20):
    tp.submit(func,i)
tp.shutdown()
# 创建进程池
import time
import os
from concurrent.futures import ProcessPoolExecutor
def func(i):
    time.sleep(1)
    print('子进程%s'%i,os.getpid())
if __name__ == '__main__':
    tp = ProcessPoolExecutor(5)
    for i in range(20):
        tp.submit(func,i)
    tp.shutdown()

8、其它补充:result获取子线程中的返回值、callback回调函数来处理子线程中代码的执行结果

# 使用result获取子线程中的返回值
import time
from threading import currentThread
from concurrent.futures import ThreadPoolExecutor
def func(i):
    time.sleep(1)
    print('子线程%s'%i,currentThread().ident)
    return i**2

tp = ThreadPoolExecutor(5)
ret_l = []
for i in range(20):
    ret = tp.submit(func,i)
    ret_l.append(ret)
for ret in ret_l:
    print(ret.result())
# 使用回调函数来处理子线程中代码的执行结果
import time
from threading import currentThread
from concurrent.futures import ThreadPoolExecutor
def func(i):
    time.sleep(1)
    print('子线程%s'%i,currentThread().ident)
    return i**2
def callback(ret):
    print(ret.result())
tp = ThreadPoolExecutor(5)
for i in range(20):
    tp.submit(func,i).add_done_callback(callback)
tp.shutdown()
posted @ 2019-01-28 22:06  pythonernoob  阅读(134)  评论(0)    收藏  举报