01/10/2018, 09:07

Lập trình với multiprocessing

Mọi người cùng chia sẻ kinh nghiệm của mình về multiprocessing đi ạ.

import time


def task(name):
    # 1 second for 1 task
    time.sleep(1)
    print('finish task : ', name)


list_task = range(10)

from multiprocessing import Process

# 10 task > 10s
for t in list_task:
    # task(t)
    Process(target=task, args=(t,)).start()

Mình mở topic nên chọn phần dễ trước

  • Không bao giờ edit biến Global, GLOBAL chỉ nên là hằng số. Vì ta không biết biến global sẽ thay đổi khi nào trong multiprocessing.

Xin kinh nghiệm của mọi người

Tao Không Ngu. viết 11:11 ngày 01/10/2018

Hi Luan.
Nhảm. (@_@!)

NG viết 11:14 ngày 01/10/2018

Thế bạn nghĩ sao thì chia sẻ, có sai thì chỉ chứ sao lại nói thế, đây là diễn đàn dạy nhay học chứ có phải diễn đàn chuyên nghiệp đâu, ai cũng có quyền hiểu sai, quan trọng là chia sẻ để biết sai mà sửa chứ.
Bạn muốn thể hiện điều gì ?

Tao Không Ngu. viết 11:15 ngày 01/10/2018

Hi Luan.
Cái gì cần là hằng số thì là hằng số cái gì không là hằng số thì không là hằng số.

NG viết 11:21 ngày 01/10/2018

Mình thích câu phát biểu của bạn, thế nhưng đây không phải là học cửu âm chân kinh trong phim Tàu.
Bạn có thực sự hiểu mình muốn nói gì không vậy ? sorry nếu mình giải thích kém hay hành văn không rõ ràng.
Mình nói đây là không nên EDIT các biến trên GLOBAL. Mọi người có thói quen gọi 1 biến trên GLOBAL rồi dùng chung cho 1 đống function, mỗi function edit 1 kiểu. Thế nhưng điều đó trong multiprocess sẽ dẫn tới những lỗi không mong muốn. Vì không biết process nào xong trước, không thể xác định cái biến của bạn bị edit thành cái dạng gì, cho nên khi khai báo GLOBAL chỉ nên là hằng số và không nên bị thay đổi bởi bất kì function nào.

Tao Không Ngu. viết 11:13 ngày 01/10/2018

Hi Luan.
Vấn đề biến toàn cục không liên qua gì đến đa tiến trình cả. Hạn chế dùng biến toàn cục là một thói quen tốt của lập trình viên trong mọi trường hợp không chỉ trong đa tiến trình.

P/S Hoặc ban bỏ phần đa tiến trình đi chỉ nói đên biến toàn cục và đóng gói dữ liệu hoặc là nói đến các vấn đề chính của đa tiến trình.

NG viết 11:22 ngày 01/10/2018

Ok, cái mình cần là những chia sẻ như thế này. Vậy lúc lập trình đa tiến trình, bạn có tâm đắc hay kinh nghiệm gì không ? Có cái gì cần tránh hoặc chú ý ?

Mình nói về biến toàn cục vì vấn đề mình gặp phải lúc lập trình liên quan tới đa tiến trình, mình không kiểm soát được nên gây ra rất nhiều lỗi, mà muốn kiểm soát thì rất rắc rối, bởi vậy mình mới gộp chung vào.

Lúc trước cũng có người nói về vụ biến toàn cục, thế nhưng lập trình bình thường thì mọi thứ êm xuôi, cho đến khi đụng vấn đề xử lý đa tiến trình này.

Tao Không Ngu. viết 11:23 ngày 01/10/2018

Hi Luan.
Mai bàn.

1 Vấn đê biến toàn cục êm khi bạn code nhỏ chí có 1 người code v.v.v… nói chung là bạn vẫn quản lý được từng dòng code.

2 Vấn đề đa luồng thì có một số cái.
2.1 Trạng thái của một tiến chình chưa được chạy, đang chạy, đã chạy, có lỗi thoát ra.
2.2 Vấn đề tương tác giữa các luồng chay luồng, dừng luồng, chờ luồng.
2.3 Vấn để truyền dữ liệu vào, lấy dữ liệu ra trao đổi dữ liệu khi đang chạy luồng.
2.4 Đồng bộ và bất đồng bộ.

P/S Mình chưa có nhiều luồng bao giờ nghe gian hồ họ đồn thế.

NG viết 11:20 ngày 01/10/2018

1 tuần rồi không có ai hưởng ứng hết, mình tự kỷ vậy. Mấy người biết thì không quan tâm, còn người quan tâm thì không biết. Hôm nay rảnh giới thiệu cho người chưa biết vậy.

Giới Thiệu
multiprocessing nói đơn giản là 1 module cho phép mình goi nhiều process con chạy song song. giúp tận dụng đc tài nguyên của máy để tăng tốc độ giải quyết vấn đề.
Ví dụ
Mình lấy ví dụ từ thớt này nhé Làm sao để lấy được các text trong và ngoài thẻ a đây hả các bác
Mô tả: Lấy tất cả nội dung của glosarry trên trang đó lưu lại dưới dạng Json.
vậy để giải quyết vấn đề mình cần mấy module sau

import requests
from bs4 import BeautifulSoup
import string
import json
import os

Mình viết 1 function, lấy nội dung cho từng chữ cái :

def list_glossary(word):
    response = requests.get("http://www.imdb.com/glossary/" + word)
    html_doc = response.text
    list_gl = ['<H3>' + e for e in html_doc.split('<H3>')]
    list_gl[list_gl.__len__() - 1] = list_gl[list_gl.__len__() - 1].split('<HR NOSHADE')[0]
    list_result = {}
    for gl in list_gl[1:]:
        soup = BeautifulSoup(gl.
                             replace('<BR>', '\n').
                             replace('</BR>', '').
                             replace('<P>', '\n').
                             replace('</P>', '')
                             , 'html.parser')
        key_gl = soup.contents[0].string
        desc = soup.get_text()
        if key_gl != None:
            key_gl = key_gl.upper()
        list_result[key_gl] = desc
    return list_result

Cách làm bình thường:
Trên web http://www.imdb.com/glossary/ mình thấy các chữ cái từ A tới Z, vậy mình sẽ phải gọi list_glossay cho từng chữ cái.

# list từ A -> Z
list_task = string.printable[36:62]
# tạo một thư mục để file vào cho gọn:
os.makedirs('./imdb')
# làm việc thôi 
 for t in list_task:
    try:
        print(t)
        data = list_glossary(t)
        try:
            dataj = json.dumps(data)
            with open(r'./imdb/' + t, 'w') as f:
                f.write(dataj)
        except:
            print('error write')
    except:
        print('Key Error ', t)

Và mình ngồi chờ nó load từng trang, xong hết với mạng nhà mình tầm 20s. Tuy đợi chờ là hạnh phúc nhưng mình trả 1 đống tiền xương máu mua con i5 với cúng tiền hàng tháng cho cái cáp quang thỉnh thoảng bị cá mập cắn thì không lý do gì mình không tận thu. Vậy nên mình xài multiprocessing.
Mình thấy cái function load dữ liệu từng trang nó độc lập với nhau, vậy sao không chạy 26 cái cùng 1 lúc thay vì ngồi đợi từng cái tiếp từng cái.
vậy nên mình viết lại :

# list từ A -> Z
list_task = string.printable[36:62]
# tạo một thư mục để file vào cho gọn:
os.makedirs('./imdb')

# mình gọi 1 lần load 1 trang là 1 task
def task(t, nbtry=1):
    try:
        print(t)
        data = list_glossary(t)
        try:
            dataj = json.dumps(data)
            with open(r'./imdb/' + t, 'w') as f:
                f.write(dataj)
        except:
            print('error write')
    # đề phòng mạng bị lỗi nên load lại thôi, nbtry : số lần thử lại
    except:
        if nbtry != 0:
            task(t, nbtry - 1)
        else:
            print('ERROR KEY ', t)

# gọi multiprocessing thần thánh ra nào
from multiprocessing import Process

# cho hết 1 đống task vào 1 list
all_processes = [Process(target=task, args=(t,)) for t in list_task]
# Cho tất cả chạy cùng 1 lúc
for p in all_processes:
    # task(t)
    p.start()
# Chờ nó xong việc
for p in all_processes:
    # task(t)
    p.join()

Xong rồi, xem kết quả thôi : lần này mình mất chưa đến 3s. nhanh hơn 10 lần

To be continue
Chắc chắn có bác sẽ thắc mắc là sao mình không để vào một file ? điều này sẽ hơi rắc rối vì là vấn đề chia sẻ dữ liệu của nhiều process chạy song song. với multiprocessing sẽ rất rắc rối và khó hiểu, sẽ mất nhiều thời gian cho người mới. Nếu thớt đc mấy bác quan tâm, mình sẽ giới thiệu tiếp. Lần sau sẽ là goless, một module theo ngôn ngữ GO, chức năng tương tự multiprocessing nhưng có cách viết dễ hiểu hơn, tiện lợi hơn, sẽ giải quyết vấn đề này.

Còn bác nào nghĩ tạo biến GLOBAL thì lạy hồn, mình nói rồi, không chạy đâu. Nếu bác nào nghĩ đến Lock() thì có khả năng đấy, nhưng đó là 1 vấn đề khác nũa của multiprocessing, có nhiều điều để bàn lắm, trong đk bài viết này mình sẽ không nói đến.

Tao Không Ngu. viết 11:16 ngày 01/10/2018

Hi NG.
Bài viết rất hữu ích. Bạn có link trên github không để minh bookmap lại khi nào cần xem lại cho tiện.
Còn về phần chia sẻ dữ liệu thì thường làm theo kiểu oop nó có dữ liệu riêng và đóng gói khá ổn. Nếu không có thể tạo struct để lưu trữ cho mỗi luồng. Cũng là một cách triển khai oop.

Đào An viết 11:07 ngày 01/10/2018

Hóng phần tiếp theo của bác . Bác làm cái blog cho ae dễ theo dõi .

NG viết 11:11 ngày 01/10/2018

Ok, khi nào rảnh mình sẽ up code lên github.

Bài liên quan
0