• python 多线程、多进程


    一、首先说下多线程、多进程用途及异同点,另外还涉及到队列的,memcache、redis的操作等:

          1、在python中,如果一个程序是IO密集的操作,使用多线程;运算密集的操作使用多进程。

               但是,其实在python中,只支持一个cpu的多线程,多个任务是切换执行的,并不能并行执行,所以有的时候,多线程并不比单线程要快,在我们的理解中,下意识的就会认为

               多线程肯定比单线程要快,其实不然,多线程只会在有线程阻塞的情况下才会起到效果,下面我们来看一个实例:

     1 import os,sys,json
     2 import threadpool,threading
     3 from collections import OrderedDict
     4 import  collections,time
     5 import random
     6 import datetime
     7 
     8 print(datetime.datetime.now())
     9 def analysed():
    10     # count_dict = {}
    11     with open('log.log') as log_file:
    12         for line in log_file.readlines():
    13             line = line.strip()
    14             if '.action?' in line:
    15                 url_api =  line.split()[6].split('.action')[0]
    16                 if url_api.startswith('http'):
    17                     count = count_dict.setdefault(url_api,0)
    18                     count  += 1
    19                     count_dict[url_api] = count
    20                 else:
    21                     continue
    22             else:
    23                 continue
    24 
    25     order_dict = collections.OrderedDict(reversed(sorted(count_dict.items(),key=lambda x: x[1])))
    26     # return order_dict
    27     for key in order_dict.keys():
    28         if '?' in key:
    29             continue
    30         else:
    31             # print(key,order_dict.get(key))
    32             pass
    33     print(datetime.datetime.now())
    34 count_dict = {}
    35 t = threading.Thread(target=analysed,args=())
    36 t.start()

               解释说明:

          首先我们看下这个脚本实现的目的,主要就是打开一个日志文件,里面是nginx访问日志,提取出来部分url,然后去重排序输出重复的url数量,(在这里我没有输出,是因为方便看时间

                      最下面的两行就是多线程,使用的是threading的Thread的方法,里面有两个参数;一个是target,用来指定调用的函数,另一个是args,用来指定传递的参数),其它的就不过多的

                      解释了,直接看运行的时间:              

            《 2016-07-22 09:56:33.716978
                2016-07-22 09:56:39.375297 》;总共用了6秒的时间,下面我们就不把所有的脚本都输出出来了,只把最后两行的内容改为analysed(count_dict),直接调用函数,然后

                      输出的结果为:                       

            《 2016-07-22 10:19:57.294831
                2016-07-22 10:20:02.549330 》;总共用时5秒

                      总结:

                            从输出的结果就可以看出来,使用多线程比使用单线程用时还要长,因为这个脚本执行的过程不会产生线程阻塞的问题,所以执行的时间都是差不多的,在这里多线程是起不到什么作

                            用的,也验证了我们上面所说的观点(线程阻塞的脚本实例就不跟大家演示了,大家只要明白这个道理就OK了)

             2、说完多线程,下面我们来聊聊线程池的作用,在说线程池之前,我们先来看下队列:

                  队列可以有多种形式(1、先进先出,2、先进后出),其实不管是那种形式,它的作用就是把任务放在里面,然后进程或者线程去取任务来执行,下面我们来定义一个队列,并列出一些常用的

                  方法:

                       myqueue = queue.Queue() ;括号里面可以有好多的参数(其中maxsize = num)指定的是队列的最大长度

                       myqueue.put() ; 括号里面放置的是任务,就是把任务放到队列中

                       myqueue.get() ; 这个方法是获取队列中的任务,如果队列中已经没有任务了,那么就会一直处于等待状态。

                       myqueue.qsize() ; 这个方法是获取队列的长度

                       myqueue.empty()  ; 如果队列为空,返回True,反之False 

                       myqueue.full() ; 如果队列满了,返回True,反之False

                       附上一个例子:

     1 import queue
     2 import threading
     3 import time
     4 
     5 q = queue.Queue(20)
     6 
     7 def productor(arg):
     8     q.put('买票')
     9 
    10 #定义有6个人在同时购票,但是三台服务器只能同时处理三个请求。
    11 for i in range(6):
    12     t = threading.Thread(target=productor,args=(i,))
    13     t.start()
    14 
    15 def consumer(arg):
    16     while True:
    17         print(arg,q.get())
    18         time.sleep(2)
    19 #描述为三台服务器在同时处理,一台服务器同一时刻只能处理一个请求
    20 for i in range(3):
    21     t = threading.Thread(target=consumer,args=(i,))
    22     t.start()

             我们先看代码,然后再列出输出的结果;这段代码的意思是说,6个月在同时买票,然后又三个线程在同时处理,买票的会把买票放在队列中,也就是q.put('买票')

                     线程处理里面threading.Thread(target=consumer,args=(i,));括号里面分别的意思分别是:target指明线程要调用的函数,args是要传递的参数,在这

                     里我们只传递第几个线程去执行任务;然后consumer函数执行去队列中取任务,也就是q.get()。看完这段代码,我们再看输出的结果:

    1 0 买票
    2 1 买票
    3 2 买票
    4 0 买票
    5 1 买票
    6 2 买票

             其实我们这样看,看的并不是很明确,其实在输出结果之后,程序还没有停止,上面我们再说队列的时候,也简单的提到了,因为,最后的q.get()会一直等待输出,

                   如果我们不想代码一直这么地的等待下去,那么我们可以在q.get()的括号里面加上超时时间,可以写成这种模式:q.get(timeout=5),等待5秒。

        2、多进程:

            2.1在这里我还想再啰嗦一句,多线程只能利用单核CPU,也就是说不管你产生多少线程,都只会在一个CPU上去调度,但是多进程的进程池可以实现多CPU调度,以前

                   的博客里,也提到过,如果你的任务是计算密集型的,就要使用多进程,其实就是因为多进程能调度多核cpu,所以比较快。

             实现线程的模块是multiprocessing,它里面也有很多的方法来实现不同的目的,其实进程和线程除了模块不同之外,它们的方法基本是都是一样的,而且含义也是

    一样的,下面我们也是先来举一个单线程的例子:

     1 import multiprocessing
     2 import time
     3 
     4 def func(msg):
     5     for i in range(3):
     6         print(msg)
     7         time.sleep(1)
     8 
     9 if __name__ == "__main__":
    10     p = multiprocessing.Process(target=func, args=("hello", ))
    11     p.start()
    12     p.join()
    13     print("Sub-process done")

              解释说明:

                    首先先说main里面的代码,先创建一个进程对象,然后调用它的start方法(表明开始执行func函数),那个p.join()在这里说一下,多线程里面也有这个方法,

                    其功能和含义是相同的,表示主进程等待子进程,在我们这段代码中,主进程就是那个print语句,等待函数执行完毕之后再执行这个print语句,下面我们来看下

                    输出的结果,那么你就恍然大明白了!!!

    hello
    hello
    hello
    Sub-process done

             2.2、进程池

                   其实进程池就是调用的multiprocessing模块中的Pool方法,然后完成进程池的目的,看代码:

     1 from multiprocessing import Pool
     2 
     3 def f1(arg):
     4     print(arg)
     5 
     6 if __name__ == '__main__':
     7     pool = Pool(5)
     8     for i in range(5):
     9         pool.apply_async(func=f1,args=(i,))
    10 
    11     pool.close()
    12     pool.join()

                  方式一样,先看代码的含义,然后再输出结果,pool对象的apply_async方法的作用就是接受任务并把它们(任务)放到进程池中去,在这里需要注意的是,

                  进程池需要关闭;然后说下那两个5的含义,第一个5是进程池最多产生5个进程,第二个5是产生五个任务,供进程去处理; 下面看输出的结果:

    0
    1
    2
    3
    4
  • 相关阅读:
    css基础教程
    网页加载-上下幕布分开
    Vue-Aixos
    webpack学习
    Vue、Element 路由跳转,左侧菜单高亮显示,页面刷新不会改变当前高亮菜单
    Vue知识概括
    JSON.parse() 与 JSON.stringify()
    Bootstrap4 样式笔记
    ES6基础语法
    V-model 双向绑定的原理是什么?
  • 原文地址:https://www.cnblogs.com/madq-py/p/5692873.html
Copyright © 2020-2023  润新知