python多线程与多进程

最近做项目的时候因为要对大批量的数据进行处理,所以立马想到的是用多线程/多进程进行加速处理,但是python中多线程和多进程还是有一些区别,因此使用的场景也有所不同。

多线程与多进程的区别

记得教科书上一句经典的对多线程和多进程的介绍:”进程是资源分配的最小单位,线程是cpu调度的最小单位“。简单说来就是,多线程共享内存,内存占用少,cpu调度简单,cpu利用率高;多进程每个进程独享内存,内存占用高,cpu调度复杂,cpu利用率低。了解了这些,如果是使用过c++或者java的程序员会很好的根据场景选择多线程/多进程,但是在python中如果也按照这种规律去选择就会有一些问题。

python多线程

python是一种解释性语言,为了安全的考虑就设计了一个GIL(Global Interpreter Lock,全局解释器锁)的东东,每个进程一个GIL,如果进程下面有多个线程,则线程竞争获取锁进行执行,那么就意味着多线程并不是并行的,每次只有一个线程在执行,也就是很多人说的python多线程是假的。

那么就会有人有疑问了,既然是假的还实现它干嘛?因为有一种任务,如果使用的cpu比较少,很多时候都是在等待做其他的事情的情况下,就可以使用多线程进行加速,例如:IO操作/网络的请求操作等。很典型的一个例子就是爬虫,很多时候爬虫都在进行http的请求,请求一般都是很耗时的,还经常会面临请求超时失败的情况,这个时候线程会让出cpu给其他任务,起到合理利用cpu的目的。

场景一:pywsgi多进程

默认情况下,pywsgi是单进程的,在请求量大的情况下会进行阻塞,如果部署的服务器是多核的,可以,开启多进程可以加大并发请求量,充分利用多核的算力:


def serve_forever(server):
    server.start_accepting()
    server._stop_event.wait()

app = Flask(__name__, static_folder='app', static_url_path='')
server = pywsgi.WSGIServer(('0.0.0.0', ASK_SERVICE_PORT), app)
server.start()
for i in range(cpu_count() * 2 + 1):
    Process(target=serve_forever, args=(server,)).start()

进程数为cpu数*2+1。

场景二:api接口异步执行

在工程中,有些api请求的执行时间很久,如果等待执行完再返回结果就会阻塞调用方,这时可以将接口做成异步的,接收到的参数后加入到队列或者redis里面,开启多线程或者多进程去执行后面的任务。

例如:提交资料接口,收到请求后将数据放到redis里面,就返回结果。

def submit_material_by_url(self, url, tree_name, node_id):
    data = {
        "url": url,
        "tree_name": tree_name,
        "node_id": node_id
    }
    self.data_redis.put_url(json.dumps(data))
    return {'err': ErrorCode.SUCCESS}

开启多线程启动下载任务。

 # 根据url下载资料处理进程
 self.process = Thread(target=self._process, name="process_submit_material_by_url")
 self.process.start()


def _process(self):
    while True:
        data = self.data_redis.get_url() 
        try:
            if data is None:
                continue
            data_dict = json.loads(data)
            url = data_dict["url"]
            tree_name = data_dict["tree_name"]
            node_id = data_dict["node_id"]
            self.query_skill_tree_view.submit_material_by_url(url, tree_name, node_id)
        except Exception as e:
            logger.error(traceback.format_exc())
            if data is not None:
                self.data_redis.put_url(data)
            time.sleep(2)

场景三:多进行分词

在对大批量的数据进行分词的时候,如果使用单进程进行处理,速度将会非常慢,往往要处理很久。这个时候可以使用多进行进行加速,分词主要使用cpu。

这里使用线程池的map操作,对分词后的结果写文件。

def _cut_data(self, processes=4):
    """对语料进行分词"""
    new_data = []
    pool = Pool(processes)
    data_list = []
    with open(self._blog_data_selected_path) as file:
        for line in file:
            data_list.append(line.strip().lower())
    new_data = pool.map(self._simple_cut, data_list)

    with open(self._blog_data_selected_seg_path, "w") as file:
        file.writelines([line + "\n" for line in new_data])

    return True

你可能感兴趣的:(NLP的应用落地,python,多进程,多线程)