Python学習ノート(3)マルチスレッドの使用

9217 ワード

この節では,マルチスレッド学習の心得を記録する.
 
Pythonはthreadモジュールを提供していますが、このモジュールの欠点は多く、例えばスレッドの終了を容易に待つことができないため、より高度なthreadingモジュールを使用しています.
 
threadingモジュールの使用には、3つのモードがあります.
1.関数によるThreadインスタンスの生成
2.関数を使用して呼び出し可能なクラスオブジェクトを生成し、Threadインスタンスを生成する
3.Threadからサブクラスを派生し、このサブクラスのインスタンスを作成する
 
関数によるThreadインスタンスの生成
 
1つ目の使用方法は最も簡単で、コードは以下の通りです.
import threading
from time import sleep

def threadFunc():
    i = 10;
    while i > 0:
        print 'i = %d' % i
        i -= 1

if __name__ == '__main__':
    t = threading.Thread(target = threadFunc)
    t.start()
    t.join()

このコードの論理は簡単で、スレッドでthreadFuncという関数を実行します.
この関数にパラメータが必要な場合は、
t = threading.Thread(target = threadFunc)

次のように、この行にパラメータを追加します.
import threading
from time import sleep

def threadFunc(i):
    while i > 0:
        print 'i = %d' % i
        i -= 1

if __name__ == '__main__':
    t = threading.Thread(target = threadFunc, args = [10])
    t.start()
    t.join()

注意argsパラメータは、メタグループまたはリストを使用する必要があります.
 
関数を使用して呼び出し可能なクラスオブジェクトを生成し、Threadインスタンスを生成します.
 
C++に関数オブジェクトがあり、あるクラスのリロード関数にオペレータを呼び出すと、そのクラスのオブジェクトが関数として使用でき、pythonにも同様のメカニズムがあります.
class Foo():
    def __call__(self):
        print 'foobar'

if __name__ == '__main__':
    f = Foo()
    f()

この例ではfはオブジェクトですが、関数として使用できます.f()が呼び出されると、解釈器はFooの__を呼び出すcall__メソッドは、C++に相当するoperator()オペレータがリロードされます.
 
もう一つのapplyに関する知識点は、
def test(i):
    print 'i = %d' % i 

if __name__ == '__main__':
    apply(test, [1])

applyはこのように関数を呼び出すことができます.このメカニズムにより,関数を格納し,適切なタイミングを選択して呼び出しに注意することができる.
from random import randint

def foo(i):
    print 'i = %d' % i 
def bar(i):
    print 'i*i = %d' % (i*i)

class Foo():
    def __call__(self, i):
        print 'foobar: %d' % i

if __name__ == '__main__':
    funcs = [foo, bar, Foo()]
    for func in funcs:
        i = randint(1, 4)
        apply(func, [i])

 
これにより、クラスに関数を格納し、クラスに__を提供できます.call__関数は、このクラスのオブジェクトでも実行できるので、このオブジェクトを利用してThreadを生成します.
#coding: utf-8
import threading
from time import sleep

class ThreadFunc(object):
    def __init__(self, func, args):
        self.func = func
        self.args = args

    def __call__(self):
        apply(self.func, self.args)

def loop(i):
    while i > 0:
        print 'i = %d' % i
        sleep(0.5)
        i -= 1

if __name__ == '__main__':
    print '           '
    t1 = ThreadFunc(loop, [5])
    t1()

    print '          '
    t2 = threading.Thread(target = t1)
    t2.start()
    t2.join()
    print '      '

    print '          '
    t3 = threading.Thread(target = ThreadFunc(loop, [3]))
    t3.start()
    t3.join()
    print '      '

t 1はThreadFuncのインスタンスであり、直接実行することもできるし、Threadインスタンスを生成するために使用することもできる.
 
Threadからサブクラスが派生し、このサブクラスのインスタンスが作成されます.
 
最も簡単な使い方は次のとおりです.
import threading
from time import sleep

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.count = 10

    def run(self):
        while self.count > 0:
            print 'i = %d' % self.count
            sleep(1)
            self.count -= 1

if __name__ == '__main__':
    t = MyThread();
    t.start()
    t.join()

Threadクラスを継承し、JavaのThreadの使用と一致するrunメソッドを上書きします.
複数のスレッドを作成するには、次のようにします.
import threading
from time import sleep

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        print 'begin .....'
        sleep(5)
        print 'end.....'

if __name__ == '__main__':
    threads = []
    for i in range(10):
        t = MyThread()
        threads.append(t)
    for t in threads:
        t.start()
    for t in threads:
        t.join()

 
しかし、現在、私たちのスレッドロジックは固定されており、第2の方法を参考にして、外部からロジックを転送し、記憶することができます.
#coding: utf-8
import threading
from time import sleep

class CustomThread(threading.Thread):
    def __init__(self, func, args):
        threading.Thread.__init__(self)
        self.func = func
        self.args = args

    def run(self):
        apply(self.func, self.args)

def loop(i):
    while i > 0:
        print 'i = %d' % i
        sleep(0.5)
        i -= 1

if __name__ == '__main__':
    t = CustomThread(loop, [10])
    t.start()
    t.join()

2つ目とは違います
1.継承を採用し、ベースクラスはThread
2.runメソッドを上書きします.call__方法
3.使用時にクラスのインスタンスを直接作成する
以上の3つは、個人的には3つ目が一番便利だと思いますが、大きなプログラムでは、このThreadを単独でモジュールにすることができます.
また、前の2つの本質は同じであり、いずれもThreadに実行可能なオブジェクトが入力される(pythonでは関数もオブジェクトである).
 
終わります.