2011-12-10 22 views
1

私は、オブジェクトのリストを保存するためにPythonでスレッドを使用する方法を学んでいます。 私はこのコードから始めている:pythonスレッド、マルチスレッドコードの実行中に生成された結果を返す方法

import threading 
import urllib 
from tempfile import NamedTemporaryFile 

singlelock = threading.Lock() 

class download(threading.Thread): 
    def __init__(self, sitecode, lista): 
     threading.Thread.__init__(self) 
     self.sitecode = sitecode 
     self.status = -1 

    def run(self): 
     url = "http://waterdata.usgs.gov/nwis/monthly?referred_module=sw&site_no=" 
     url += self.sitecode 
     url += "&PARAmeter_cd=00060&partial_periods=on&format=rdb&submitted_form=parameter_selection_list" 
     tmp = NamedTemporaryFile(delete=False) 
     urllib.urlretrieve(url, tmp.name) 
     print "loaded Monthly data for sitecode : ", self.sitecode 
     lista.append(tmp.name) 
     print lista 

sitecodelist = ["01046500", "01018500", "01010500", "01034500", "01059000", "01066000", "01100000"] 
lista = [] 


for k in sitecodelist: 
    get_data = download(k,lista) 
    get_data.start() 

私はそれを返すためにトリングいながら、それはちょうど、スレッドの実行中に生成されたリストを出力します。私の問題の解決策であるように思わ

ドキュメントを読むしようとすると、私はthreading.Lock()とそのメソッドacquire()を使用する方法について見てrelease()よ... が、私はそれを実装する方法を理解することは本当に遠いですよ私の例のコードでは。

ありがとうございます。

答えて

3

まず、すべてのスレッドがhttp://en.wikipedia.org/wiki/Thread_%28computer_science%29であることをすばやく確認する必要があります。

スレッドはメモリを共有します。これは簡単なはずです!スレッドに関しても良い、悪いことは、簡単で危険です! (OSにも軽量です)。

さて、CPythonのと、パイソンを使用した場合は、グローバルインタプリタロックを理解しておく必要があります。

また

http://docs.python.org/glossary.html#term-global-interpreter-lock

http://docs.python.org/library/threading.htmlから:

はCPythonの実装の詳細:原因グローバルにInterpreter Lock in CPythonはただ1つのスレッドしかPythonコードを実行できません( 特定のパフォーマンス指向ライブラリがこれを克服するかもしれませんが 制限)。マルチコアマシンの計算リソースを でより良くするには、 マルチプロセッシングを使用することをお勧めします。しかし、 複数のI/Oバウンドタスクを同時に実行する場合は、スレッド化は適切なモデルです。

これはどういう意味ですか?あなたのタスクがIOスレッドでない場合、OSから何かを得ることはできません。なぜなら、Pythonコードを実行するたびに、グローバルロックを持っていて、他のスレッドがそれを取得することができないため、 。 IOバインドされたタスクでは、OSはIOが完了するのを待っている間にグローバルロックが解除されるため、他のスレッドをスケジュールします。 GILの下にないコードに呼び出すことができますが、この場合スレッド化もうまく機能します(上記の「パフォーマンス指向ライブラリ」への参照)。

ありがたいことに、Pythonは管理します共有メモリは単純なタスクであり、これを行う方法については既に良い文書がありますが、それを見つけるにはちょっとした手間がかかります。ご不明な点がございましたら、お知らせください。

In [83]: import _threading_local 

In [84]: help(_threading_local) 
Help on module _threading_local: 

NAME 
    _threading_local - Thread-local objects. 

FILE 
    /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_threading_local.py 

MODULE DOCS 
    http://docs.python.org/library/_threading_local 

DESCRIPTION 
    (Note that this module provides a Python version of the threading.local 
    class. Depending on the version of Python you're using, there may be a 
    faster one available. You should always import the `local` class from 
    `threading`.) 

    Thread-local objects support the management of thread-local data. 
    If you have data that you want to be local to a thread, simply create 
    a thread-local object and use its attributes: 

     >>> mydata = local() 
     >>> mydata.number = 42 
     >>> mydata.number 
     42 

    You can also access the local-object's dictionary: 

     >>> mydata.__dict__ 
     {'number': 42} 
     >>> mydata.__dict__.setdefault('widgets', []) 
     [] 
     >>> mydata.widgets 
     [] 

    What's important about thread-local objects is that their data are 
    local to a thread. If we access the data in a different thread: 

     >>> log = [] 
     >>> def f(): 
     ...  items = mydata.__dict__.items() 
     ...  items.sort() 
     ...  log.append(items) 
     ...  mydata.number = 11 
     ...  log.append(mydata.number) 

     >>> import threading 
     >>> thread = threading.Thread(target=f) 
     >>> thread.start() 
     >>> thread.join() 
     >>> log 
     [[], 11] 

    we get different data. Furthermore, changes made in the other thread 
    don't affect data seen in this thread: 

     >>> mydata.number 
     42 

    Of course, values you get from a local object, including a __dict__ 
    attribute, are for whatever thread was current at the time the 
    attribute was read. For that reason, you generally don't want to save 
    these values across threads, as they apply only to the thread they 
    came from. 

    You can create custom local objects by subclassing the local class: 

     >>> class MyLocal(local): 
     ...  number = 2 
     ...  initialized = False 
     ...  def __init__(self, **kw): 
     ...   if self.initialized: 
     ...    raise SystemError('__init__ called too many times') 
     ...   self.initialized = True 
     ...   self.__dict__.update(kw) 
     ...  def squared(self): 
     ...   return self.number ** 2 

    This can be useful to support default values, methods and 
    initialization. Note that if you define an __init__ method, it will be 
    called each time the local object is used in a separate thread. This 
    is necessary to initialize each thread's dictionary. 

    Now if we create a local object: 

     >>> mydata = MyLocal(color='red') 

    Now we have a default number: 

     >>> mydata.number 
     2 

    an initial color: 

     >>> mydata.color 
     'red' 
     >>> del mydata.color 

    And a method that operates on the data: 

     >>> mydata.squared() 
     4 

    As before, we can access the data in a separate thread: 

     >>> log = [] 
     >>> thread = threading.Thread(target=f) 
     >>> thread.start() 
     >>> thread.join() 
     >>> log 
     [[('color', 'red'), ('initialized', True)], 11] 

    without affecting this thread's data: 

     >>> mydata.number 
     2 
     >>> mydata.color 
     Traceback (most recent call last): 
     ... 
     AttributeError: 'MyLocal' object has no attribute 'color' 

    Note that subclasses can define slots, but they are not thread 
    local. They are shared across threads: 

     >>> class MyLocal(local): 
     ...  __slots__ = 'number' 

     >>> mydata = MyLocal() 
     >>> mydata.number = 42 
     >>> mydata.color = 'red' 

    So, the separate thread: 

     >>> thread = threading.Thread(target=f) 
     >>> thread.start() 
     >>> thread.join() 

    affects what we see: 

     >>> mydata.number 
     11 

    >>> del mydata 

あなたのスタイルを上記のように使用した例です。

In [40]: class TestThread(threading.Thread): 
    ...:  report = list() #shared across threads 
    ...:  def __init__(self): 
    ...:   threading.Thread.__init__(self) 
    ...:   self.io_bound_variation = random.randint(1,100) 
    ...:  def run(self): 
    ...:   start = datetime.datetime.now() 
    ...:   print '%s - io_bound_variation - %s' % (self.name, self.io_bound_variation) 
    ...:   for _ in range(0, self.io_bound_variation): 
    ...:    with open(self.name, 'w') as f: 
    ...:     for i in range(10000): 
    ...:      f.write(str(i) + '\n') 
    ...:   print '%s - finished' % (self.name) 
    ...:   end = datetime.datetime.now() 
    ...:   print '%s took %s time' % (self.name, end - start) 
    ...:   self.report.append(end - start) 
    ...:    

出力を伴う3つのスレッドの実行。

In [43]: threads = list() 
     ...: for i in range(3): 
     ...:  t = TestThread() 
     ...:  t.start() 
     ...:  threads.append(t) 
     ...: 
     ...: for thread in threads: 
     ...:  thread.join() 
     ...:  
     ...: for thread in threads: 
     ...:  print thread.report 
     ...:  
    Thread-28 - io_bound_variation - 76 
    Thread-29 - io_bound_variation - 83 
    Thread-30 - io_bound_variation - 80 
    Thread-28 - finished 
    Thread-28 took 0:00:08.173861 time 
    Thread-30 - finished 
    Thread-30 took 0:00:08.407255 time 
    Thread-29 - finished 
    Thread-29 took 0:00:08.491480 time 
    [datetime.timedelta(0, 5, 733093), datetime.timedelta(0, 6, 253811), datetime.timedelta(0, 6, 440410), datetime.timedelta(0, 4, 342053), datetime.timedelta(0, 5, 520407), datetime.timedelta(0, 5, 948238), datetime.timedelta(0, 8, 173861), datetime.timedelta(0, 8, 407255), datetime.timedelta(0, 8, 491480)] 
    [datetime.timedelta(0, 5, 733093), datetime.timedelta(0, 6, 253811), datetime.timedelta(0, 6, 440410), datetime.timedelta(0, 4, 342053), datetime.timedelta(0, 5, 520407), datetime.timedelta(0, 5, 948238), datetime.timedelta(0, 8, 173861), datetime.timedelta(0, 8, 407255), datetime.timedelta(0, 8, 491480)] 
    [datetime.timedelta(0, 5, 733093), datetime.timedelta(0, 6, 253811), datetime.timedelta(0, 6, 440410), datetime.timedelta(0, 4, 342053), datetime.timedelta(0, 5, 520407), datetime.timedelta(0, 5, 948238), datetime.timedelta(0, 8, 173861), datetime.timedelta(0, 8, 407255), datetime.timedelta(0, 8, 491480)] 

レポートに3つ以上の要素が含まれているのはなぜですか?これは、インタープリターでループコードを3回実行したためです。私がこの「バグ」を修正したいのであれば、実行する前に共有変数を空のリストに設定する必要があります。

TestThread.report = list() 

スレッドが扱いにくくなる理由を示します。

2

これは直接あなたの質問に答えていないが、これは代わりにmultiprocessingモジュールを使用して回避策です:

from multiprocessing import Pipe, Process 
import urllib 
from tempfile import NamedTemporaryFile 


def download(conn, sitecodelist): 
    lista = [] 
    for k in sitecodelist: 
     url = 'http://waterdata.usgs.gov/nwis/monthly?referred_module=sw&site_no=' 
     url += k 
     url += '&PARAmeter_cd=00060&partial_periods=on&format=rdb&submitted_form=parameter_selection_list' 
     tmp = NamedTemporaryFile(delete=False) 
     urllib.urlretrieve(url, tmp.name) 
     print 'loaded Monthly data for sitecode : ', k 
     lista.append(tmp.name) 
    conn.send(lista) 

sitecodelist = ['01046500', '01018500', '01010500', '01034500', '01059000', '01066000', '01100000'] 

parent, child = Pipe() 
process = Process(target=download, args=(child, sitecodelist)) 
process.start() 

data = parent.recv() 
print 'Data: ', data 
process.join() 

そして、念のため、マルチプロセッシングを使用したり、Pythonスクリプトでスレッドについて、この質問:multiprocess or threading in python?

希望に役立ちます!

+0

これにより、1つのサブプロセスしか生成されません。 OPの例は、サイトコードリストのすべてのサイトコードのスレッドを生成します。 –

関連する問題