线程与进程

线程与进程

Python GIL:
            是在实现Python解析器(CPython)时所引入的一个概念。为了保证多线程访问数据的安全,CPython解释器只支持一个线程运行
            所以即使是多线程的任务,实际上也只能是一个线程一个线程的去执行,并不是真正意义上的多线程。解决方案为多进程或协成处理。

计算密集型:
    计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。
    这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
    计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。
IO密集型:
    第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务。
    这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。
    对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
    IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。
    对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。

    1、如果任务是IO密集型任务,可以用多线程;
    2、如果任务是计算密集型任务,可以通过改C语言解决

1、多线程任务

1、直接调用式多线程
import time
import threading
def music(func):
#线程1任务函数
    for i in range(2):
        print ("Begin listening to %s. %s" %(func,ctime()))
        sleep(4)
        print("end listening %s"%ctime())
def move(func):
#线程2任务函数
    for i in range(2):
        print ("Begin watching at the %s! %s" %(func,ctime()))
        sleep(5)
        print('end watching %s'%ctime())
threads = []
#线程列表
t1 = threading.Thread(target=music,args=('七里香',))
#创建线程对象,添加线程任务music,并给任务传递参数
threads.append(t1)
#添加到线程列表
t2 = threading.Thread(target=move,args=('阿甘正传',))
#创建线程对象,添加线程任务move,并给任务传递参数
threads.append(t2)
#添加到线程列表
if __name__ == '__main__':
    for t in threads:
#循环线程列表
        # t.setDaemon(True)
#为当前线程开启守护,必须在start前配置,表示不等待当前线程运行结束,当主程序结束时立即停止该线程运行
        t.start()
#启动线程
        # t.join()
#线程启动后保持阻塞,待线程执行完毕再继续往下执行
    print ("all over %s" %ctime())

#setDaemon(True):
    将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。
    这个方法基本和join是相反的。当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。
    但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦 

#join():
在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
thread 模块提供的其他方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
# 除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
# run(): 用以表示线程活动的方法。
# start():启动线程活动。
# join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。
2、继承式多线程
import time
import threading
class MyThread(threading.Thread):
#通过继承线程类来创建自己的线程
    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num = num
     def run(self):#定义每个线程要运行的函数
         print("running on number:%s" %self.num)
         time.sleep(3)
if __name__ == '__main__':
    t1 = MyThread(1)
#实例化线程1
    t2 = MyThread(2)
#实例化线程2
    t1.start()
#启动线程1,调用run方法
    t2.start()#启动线程2,调用run方法

3、线程锁
import threading
def foo():
    global num
#声明num为外部全局变量
    r.acquire()
#开启线程锁,此段一直运行到关闭线程锁
    temp = num
    print('ok')
    num = temp - 1
    r.release()
#关闭线程锁,其他线程可使用num数据

num = 100
threadingList = []
r = threading.Lock()
#实例化线程锁对象
for i in range(10):
    t = threading.Thread(target=foo)
    t.start()
    threadingList.append(t)
for t in threadingList:
    t.join()#等待所有线程结束
print(num)

4、线程递归锁:入栈式加锁,先进后出
r = threading.RLock()
r.acquire()#加锁1
r.acquire()#加锁2
r.acquire()#加锁3
print('lock')
r.release()#解锁3
r.release()#解锁2
r.release()#解锁1

5、信号量:即并行锁,可同时加多把锁,每把锁销毁后可允许下个任务调用该锁
semaphore = threading.BoundedSemaphore(5)
#实例化5把锁
semaphore.acquire()
print('semaphore')
semaphore.release()

6、变量同步锁
有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition对象用于条件变量线程的支持,它除了能提供RLock()或Lock()的方法外,
还提供了wait()、notify()、notifyAll()方法。
    lock_con = threading.Condition([Lock/Rlock])#锁是可选选项,不传入锁,对象自动创建一个RLock().
    wait():#条件不满足时调用,线程会释放锁并进入等待阻塞;
    notify():#条件创造后调用,通知等待池激活一个线程;
    notifyAll():#条件创造后调用,通知等待池激活所有线程。
import threading,time
from random import randint
class Producer(threading.Thread):
    def run(self):
        global L
        while True:
            val=randint(0,100)
            print('生产者',self.name,":Append"+str(val),L)
            if lock_con.acquire():
                L.append(val)
                lock_con.notify()
#这里通知wait状态的线程
                lock_con.release()
            time.sleep(3)
class Consumer(threading.Thread):
    def run(self):
        global L
        while True:
                lock_con.acquire()
                if len(L)==0:
                    lock_con.wait()
#wait状态释放线程锁,并等待notify激活该wait状态,如果激活则从该线程加锁处继续执行,不会打印下面的test
                    print('test')
                print('消费者',self.name,":Delete"+str(L[0]),L)
                del L[0]
                lock_con.release()
                time.sleep(0.25)

if __name__=="__main__":
    L=[]
    lock_con=threading.Condition()
    threads=[]
    for i in range(5):
        threads.append(Producer())
    threads.append(Consumer())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

7、同步条件Event
条件同步和条件变量同步差不多意思,只是少了锁功能,因为条件同步设计于不访问共享资源的条件环境。event=threading.Event():条件环境对象,初始值 为False;
    event.isSet():返回event的状态值;
    event.wait():如果 event.isSet()==False将阻塞线程;
    event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    event.clear():恢复event的状态值为False。
import threading,time
class Boss(threading.Thread):
    def run(self):
        print("BOSS:今晚大家都要加班到22:00。")
        event.isSet() or event.set()
        time.sleep(5)
        print("BOSS:<22:00>可以下班了。")
        event.isSet() or event.set()
class Worker(threading.Thread):
    def run(self):
        event.wait()
        print("Worker:哎……命苦啊!")
        time.sleep(0.25)
        event.clear()
        event.wait()
        print("Worker:OhYeah!")
if __name__=="__main__":
    event=threading.Event()
    threads=[]
    for i in range(5):
        threads.append(Worker())
    threads.append(Boss())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

2、队列

Python Queue模块有三种队列及构造函数:
    1、Python Queue模块的FIFO队列先进先出。  class queue.Queue(maxsize)
    2、LIFO类似于堆,即先进后出。             class queue.LifoQueue(maxsize)
    3、还有一种是优先级队列级别越低越先出来。   class queue.PriorityQueue(maxsize)
此包中的常用方法(q = Queue.Queue()):
    q.qsize() 返回队列的大小
    q.empty() 如果队列为空,返回True,反之False
    q.full() 如果队列满了,返回True,反之False
    q.full 与 maxsize 大小对应
    q.get([block[, timeout]]) 获取队列,timeout等待时间
    q.get_nowait() 相当q.get(False)
    非阻塞 q.put(item) 写入队列,timeout等待时间
    q.put_nowait(item) 相当q.put(item, False)
    q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
    q.join() 实际上意味着等到队列为空,再执行别的操作


import queue
q = queue.Queue(maxsize = 10)
#先进先出队列,默认为0,表示队列无限长
q.put('cat')
#管道进入第一个成员
q.put('dog')#管道进入第二个成员
q.put('mouse',0)#
默认是1阻塞状态,管道满了再入则阻塞;设为0后管道满了则报异常
print(q.get())#cat
,管道第一个出列
print(q.get())#dog,管道第二个出列
print(q.get())#mouse,管道第三个出列
print(q.get(),0)#
默认是1阻塞状态,管道空了再取则阻塞;设为0后管道满了则报异常

进程

1、直接调用式多进程

import time
from multiprocessing import Process
#引入进程模块
def f(name):
    time.sleep(2)
    print('hello', name)
if __name__ == '__main__':
    processList = []
    for p in range(3):
        p = Process(target=f, args=('bob',))
#实例化进程对象,绑定进程任务函数和传递参数
        p.start()
#启动进程
        processList.append(p)
    for p in processList:
        p.join()#防止僵尸进程,必须等待子进程结束

2、面向对象式多进程

import time
from multiprocessing import Process
#引入进程模块
class MyProcess(Process):
    def __init__(self,name):
        super(MyProcess,self).__init__()#先执行父类的init方法
        self.name = name#self.name默认是进程名:MyProcess_1,传递来的name是对进程名重写命名
    def run(self)
        time.sleep(2)
        print('hello', self.name)
if __name__ == '__main__':
#多进程时win下必须有本条件语句,Linux可选
    processList = []
    for i in range(3):
        p = MyProcess('bob')
#实例化进程对象,绑定进程任务函数和传递参数
        p.start()
#启动进程
        processList.append(p)
    for p in processList:
        p.join()#防止僵尸进程,必须等待子进程结束

3、多进程通信之消息Queues

from multiprocessing import Process, Queue
def f(q,i):
    q.put([i])
#调用父进程消息q添加消息
 
if __name__ == '__main__':
    q = Queue()#创建父进程消息
    processList = []
    for i in range(3):
        p = Process(target=f, args=(q,i,))
#实例化子进程对象,绑定进程任务函数和传递参数消息q和变量
        p.start()
#启动进程
        processList.append(p)
    for p in processList:
        p.join()#防止僵尸进程,必须等待子进程结束
    print(q.get())
#获取消息信息
    print(q.get())
#获取消息信息
    print(q.get())#获取消息信息

4、多进程通信之管道Pipe

from multiprocessing import Process, Pipe
def f(conn):
    conn.send('inset')
#子进程给子进程发信息:inset
    print(conn.recv())#子进程接收父进程信息:outset
    conn.close()
if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
#创建管道,返回父进程与子进程通信的两个端子
    p = Process(target=f, args=(child_conn,))
#实例化子进程对象,并传参管道端子
    p.start()
    print(parent_conn.recv())#父进程接受子进程信息:inset
    parent_conn.send('outset')#父进程给子进程发送outset
    p.join()

5、多进程通信之数据共享Manage

 

from multiprocessing import Process, Manager
def f(d, l, i):
    d[i] = i#对主进程字典操作
    l.append(i)
#对主进程列表操作 
if __name__ == '__main__':
    with Manager() as manager:
#实例化manager对象,类似文件操作,自动关闭
        d = manager.dict()
#实例化字典对象
        l = manager.list()
#实例化列表对象
        p_list = []
        for i in range(10):
            p = Process(target=f, args=(d, l, i))
#实例化子进程并传递字典和列表参数
            p.start()
            p_list.append(p)
        for res in p_list:
            res.join()
        print(d)
#{2: 2, 1: 1, 4: 4, 0: 0, 3: 3, 6: 6, 5: 5, 9: 9, 7: 7, 8: 8}
        print(l)#[2, 1, 4, 0, 3, 6, 5, 9, 7, 8]

 

协程

 

 

1、greenlet

greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
from greenlet import greenlet
def test1():
    print(12)
    gr2.switch()
#Switch切换,实例化对象每调用一次Switch方法,就执行一节到Switch语句
    print(34)
    gr2.switch()
def test2():
    print(56)
    gr1.switch()
    print(78)
gr1 = greenlet(test1)
#实例化对象,类似迭代器,不是执行函数
gr2 = greenlet(test2)
#实例化对象,类似迭代器,不是直线函数
gr1.switch()#执行gr1的Switch方法,输出12;然后调用gr2的Switch方法,输出56;
            #然后调用gr1的Switch方法,输出34;然后调用gr2的Switch方法,输出78

2、gevent

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 
Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

import gevent
def func1():
    print('\033[31;1m李闯在跟海涛搞...\033[0m')
    gevent.sleep(2)
#此时默认IO阻塞,跳转到其他任务
    print('\033[31;1m李闯又回去跟继续跟海涛搞...\033[0m')
def func2():
    print('\033[32;1m李闯切换到了跟海龙搞...\033[0m')
    gevent.sleep(1)
#此时默认IO阻塞,跳转到其他任务
    print('\033[32;1m李闯搞完了海涛,回来继续跟海龙搞...\033[0m')

gevent.joinall([
#通过joinall方法将两个任务联系起来,当一个任务阻塞住事,执行两一个任务
    gevent.spawn(func1),
#添加任务一
    gevent.spawn(func2),
#添加任务二,还可以加其他任务
])


import gevent
import urllib
from gevent import monkey
monkey.patch_all()
#监听IO阻塞,随时切换任务

def f(url):
    print('GET:%s'%url)
    resp = urllib.urlopen(url)
    data = resp.read()
    with open('xiaohuar.html','wb') as fp:
        fp.write(data)
gevent.joinall([
#多任务爬虫,若IO阻塞则切换任务
    gevent.spawn(f,'www.xiaohuar.com'),
    gevent.spawn(f,'www.baidu.com'),
])
ng import Process, Queue
def f(q,i):
    q.put([i])
#调用父进程消息q添加消息
 
if __name__ == '__main__':
    q = Queue()#创建父进程消息
    processList = []
    for i in range(3):
        p = Process(target=f, args=(q,i,))
#实例化子进程对象,绑定进程任务函数和传递参数消息q和变量
        p.start()
#启动进程
        processList.append(p)
    for p in processList:
        p.join()#防止僵尸进程,必须等待子进程结束
    print(q.get())
#获取消息信息
    print(q.get())
#获取消息信息
    print(q.get())#获取消息信息
posted @ 2022-11-20 09:31  大碗麻辣烫  阅读(15)  评论(0编辑  收藏  举报