एकाधिक प्रक्रियाओं से एकल फ़ाइल प्रसंस्करण


82

मेरे पास एक बड़ी पाठ फ़ाइल है जिसमें मैं प्रत्येक पंक्ति को संसाधित करना चाहता हूं (कुछ संचालन करना) और उन्हें डेटाबेस में संग्रहीत करना। चूंकि एक एकल सरल कार्यक्रम बहुत लंबा हो रहा है, मैं चाहता हूं कि इसे कई प्रक्रियाओं या थ्रेड्स के माध्यम से किया जाए। प्रत्येक थ्रेड / प्रक्रिया को उस एकल फ़ाइल से अलग-अलग डेटा (अलग-अलग लाइनें) पढ़ना चाहिए और अपने डेटा (लाइनों) के टुकड़े पर कुछ संचालन करना चाहिए और उन्हें डेटाबेस में रखना चाहिए ताकि अंत में, मेरे पास संसाधित डेटा का पूरा हो और मेरा डेटाबेस मैं डेटा की जरूरत के साथ फेंक दिया है।

लेकिन मैं यह पता लगाने में सक्षम नहीं हूं कि यह कैसे दृष्टिकोण है।


3
अच्छा प्रश्न। मुझे भी यह संदेह था। हालाँकि मैं फाइल को छोटी फाइलों में तोड़ने के विकल्प के साथ गया था :)
सुशांत गुप्ता

जवाबों:


109

आप जो खोज रहे हैं वह निर्माता / उपभोक्ता पैटर्न है

मूल सूत्रण उदाहरण

यहां थ्रेडिंग मॉड्यूल (मल्टीप्रोसेसिंग के बजाय) का उपयोग करके एक बुनियादी उदाहरण दिया गया है

import threading
import Queue
import sys

def do_work(in_queue, out_queue):
    while True:
        item = in_queue.get()
        # process
        result = item
        out_queue.put(result)
        in_queue.task_done()

if __name__ == "__main__":
    work = Queue.Queue()
    results = Queue.Queue()
    total = 20

    # start for workers
    for i in xrange(4):
        t = threading.Thread(target=do_work, args=(work, results))
        t.daemon = True
        t.start()

    # produce data
    for i in xrange(total):
        work.put(i)

    work.join()

    # get the results
    for i in xrange(total):
        print results.get()

    sys.exit()

आप थ्रेड के साथ फ़ाइल ऑब्जेक्ट साझा नहीं करेंगे। आप आपूर्ति करके उनके लिए काम का उत्पादन करेंगे डेटा की लाइनों के साथ कतार । फिर प्रत्येक थ्रेड एक लाइन उठाएगा, इसे प्रोसेस करेगा, और फिर इसे कतार में लौटाएगा।

डेटा साझा करने के लिए मल्टीप्रोसेसिंग मॉड्यूल में निर्मित कुछ और उन्नत सुविधाएं हैं , जैसे सूचियां और विशेष प्रकार की कतार । मल्टीप्रोसेसिंग बनाम थ्रेड्स का उपयोग करने के लिए ट्रेड-ऑफ़ हैं और यह इस बात पर निर्भर करता है कि आपका काम सीपीयू बाध्य है या आईओ बाध्य है।

बेसिक मल्टीप्रोसेसिंग।पूल उदाहरण

यहाँ एक बहुप्रतिष्ठित पूल का वास्तव में मूल उदाहरण है

from multiprocessing import Pool

def process_line(line):
    return "FOO: %s" % line

if __name__ == "__main__":
    pool = Pool(4)
    with open('file.txt') as source_file:
        # chunk the work into batches of 4 lines at a time
        results = pool.map(process_line, source_file, 4)

    print results

एक पूल एक सुविधा वस्तु है जो अपनी प्रक्रियाओं का प्रबंधन करता है। चूंकि एक खुली फ़ाइल इसकी लाइनों पर पुनरावृति कर सकती है, आप इसे पास कर सकते हैं pool.map(), जो इस पर लूप करेगा और कार्यकर्ता फ़ंक्शन को लाइनें वितरित करेगा। जब यह पूरा हो जाए तो मैप ब्लॉक और रिटर्न कर देता है। ज्ञात हो कि यह एक अति सरलीकृत उदाहरण है, और यह किpool.map() पूरा करने से पहले एक बार में अपनी पूरी फ़ाइल को मेमोरी में पढ़ने जा रहा है। यदि आपको बड़ी फ़ाइलों की उम्मीद है, तो इसे ध्यान में रखें। निर्माता / उपभोक्ता सेटअप डिजाइन करने के लिए और अधिक उन्नत तरीके हैं।

मैनुअल "पूल" सीमा और रेखा के साथ फिर से छँटाई

यह पूल.मैप का एक मैनुअल उदाहरण है , लेकिन एक बार में एक पूरे चलने योग्य का उपभोग करने के बजाय, आप एक कतार का आकार निर्धारित कर सकते हैं ताकि आप केवल टुकड़े टुकड़े करके इसे जितनी जल्दी हो सके खिला सकें। मैंने लाइन नंबरों को भी जोड़ा ताकि आप उन्हें ट्रैक कर सकें और यदि चाहें तो बाद में उन्हें संदर्भित कर सकें।

from multiprocessing import Process, Manager
import time
import itertools 

def do_work(in_queue, out_list):
    while True:
        item = in_queue.get()
        line_no, line = item

        # exit signal 
        if line == None:
            return

        # fake work
        time.sleep(.5)
        result = (line_no, line)

        out_list.append(result)


if __name__ == "__main__":
    num_workers = 4

    manager = Manager()
    results = manager.list()
    work = manager.Queue(num_workers)

    # start for workers    
    pool = []
    for i in xrange(num_workers):
        p = Process(target=do_work, args=(work, results))
        p.start()
        pool.append(p)

    # produce data
    with open("source.txt") as f:
        iters = itertools.chain(f, (None,)*num_workers)
        for num_and_line in enumerate(iters):
            work.put(num_and_line)

    for p in pool:
        p.join()

    # get the results
    # example:  [(1, "foo"), (10, "bar"), (0, "start")]
    print sorted(results)

1
यह अच्छा है, लेकिन क्या होगा यदि प्रसंस्करण I / O बाध्य है? उस मामले में, समानतावाद चीजों को गति देने के बजाय धीमा कर सकता है। एक सिंगल डिस्क ट्रैक के भीतर का दृश्य इंटरट्रैक सीक की तुलना में बहुत तेज होता है, और आई / ओ को समानांतर रूप से करने से इंटरट्रैक सीक्स का परिचय होता है जो अन्यथा एक क्रमिक I / O लोड होगा। समानांतर I / O से कुछ लाभ प्राप्त करने के लिए, कभी-कभी यह RAID दर्पण का उपयोग करने में काफी मदद करता है।
user1277476

2
@ jwillis0720 - ज़रूर। (None,) * num_workersश्रमिकों की संख्या के आकार के बराबर कोई भी मूल्य का टपल नहीं बनाता है। ये प्रहरी मूल्य हैं जो प्रत्येक सूत्र को छोड़ने के लिए कहते हैं क्योंकि कोई और काम नहीं है। itertools.chainसमारोह देना तुम कुछ भी कॉपी करने के लिए बिना एक आभासी अनुक्रम में एक साथ कई दृश्यों डाल है। तो हमें जो मिलता है वह यह है कि पहले यह फाइल में मौजूद लाइनों पर लूप करता है, और फिर नो वैल्यूज़।
जीडी

2
यह मेरे प्रोफेसर की तुलना में बेहतर बताया गया है, बहुत अच्छा +1।
लाइकेड

1
@ @, मैंने अपने पाठ को थोड़ा और स्पष्ट करने के लिए संपादित किया है। अब यह बताता है कि मध्य उदाहरण आपके संपूर्ण फ़ाइल डेटा को एक बार में मेमोरी में खिसकाने जा रहा है, जो कि यदि आप वर्तमान में उपलब्ध राम की मात्रा से बड़ा है तो समस्या हो सकती है। फिर मैं 3 उदाहरण में दिखाता हूं कि लाइन से लाइन कैसे जाना है, ताकि एक ही बार में पूरी फ़ाइल का उपभोग न करें।
jdi

1
@ ℕʘʘḆḽḘ पूल के लिए डॉक्स पढ़ते हैं। यह कहता है कि यह चलने योग्य को विखंडू में विभाजित करेगा और श्रमिकों को प्रस्तुत करेगा। तो यह स्मृति में सभी लाइनों का उपभोग खत्म हो जाएगा। हाँ एक समय में एक पंक्ति को पुनरावृत्त करना मेमोरी कुशल है, लेकिन यदि आप उन सभी लाइनों को स्मृति में रखते हैं, तो आप पूरी फ़ाइल को पढ़ने के लिए वापस आ जाते हैं।
jdi

9

यहाँ एक बहुत ही बेवकूफाना उदाहरण है जिसे मैंने पकाया है:

import os.path
import multiprocessing

def newlinebefore(f,n):
    f.seek(n)
    c=f.read(1)
    while c!='\n' and n > 0:
        n-=1
        f.seek(n)
        c=f.read(1)

    f.seek(n)
    return n

filename='gpdata.dat'  #your filename goes here.
fsize=os.path.getsize(filename) #size of file (in bytes)

#break the file into 20 chunks for processing.
nchunks=20
initial_chunks=range(1,fsize,fsize/nchunks)

#You could also do something like:
#initial_chunks=range(1,fsize,max_chunk_size_in_bytes) #this should work too.


with open(filename,'r') as f:
    start_byte=sorted(set([newlinebefore(f,i) for i in initial_chunks]))

end_byte=[i-1 for i in start_byte] [1:] + [None]

def process_piece(filename,start,end):
    with open(filename,'r') as f:
        f.seek(start+1)
        if(end is None):
            text=f.read()
        else: 
            nbytes=end-start+1
            text=f.read(nbytes)

    # process text here. createing some object to be returned
    # You could wrap text into a StringIO object if you want to be able to
    # read from it the way you would a file.

    returnobj=text
    return returnobj

def wrapper(args):
    return process_piece(*args)

filename_repeated=[filename]*len(start_byte)
args=zip(filename_repeated,start_byte,end_byte)

pool=multiprocessing.Pool(4)
result=pool.map(wrapper,args)

#Now take your results and write them to the database.
print "".join(result)  #I just print it to make sure I get my file back ...

यहाँ मुश्किल हिस्सा यह सुनिश्चित करने के लिए है कि हम फ़ाइल को नईलाइन वर्णों पर विभाजित करें ताकि आप किसी भी लाइन को याद न करें (या केवल आंशिक लाइनों को पढ़ें)। फिर, प्रत्येक प्रक्रिया पढ़ती है कि यह फ़ाइल का हिस्सा है और एक ऑब्जेक्ट देता है जिसे मुख्य धागे द्वारा डेटाबेस में डाला जा सकता है। बेशक, आपको इस भाग को भी करने की आवश्यकता हो सकती है ताकि आपको एक ही बार में सारी जानकारी याद में न रखनी पड़े। (यह काफी आसानी से पूरा हो गया है - बस "आर्ग्स" सूची को एक्स चंक्स और कॉल में विभाजित करें pool.map(wrapper,chunk) - यहां देखें )


-3

अच्छी तरह से एक बड़ी फ़ाइल को कई छोटी फ़ाइलों में तोड़ दें और उनमें से प्रत्येक को अलग-अलग थ्रेड्स में संसाधित करें।


ऐसा नहीं है कि ओपी चाहता है !! लेकिन सिर्फ एक विचार के लिए ... बुरा नहीं है।
DRPK
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.