簡體   English   中英

在 class 中實例化線程

[英]Instantiate threading within a class

我在class中有一個class並想在第二個class中激活threading功能。 本質上,下面的腳本是我的正確項目的可復制模板。

當我使用@threading時,我發現showit不可迭代,因此tp.map認為我沒有列表。

但是,當我運行時:

if __name__ == '__main__':
    tp = ThreadPoolExecutor(5)
    print(tp.map(testit(id_str).test_first, id_int))
    for values in tp.map(testit(id_str).test_first, id_int):
        values

我沒有遇到任何問題,除此之外我希望預期的 output 打印出列表中的每個數字。 但是,我想在class內實現這個。

類似於以下內容:

from concurrent.futures import ThreadPoolExecutor
from typing import List

id_str = ['1', '2', '3', '4', '5']
id_int = [1, 2, 3, 4, 5]

def threaded(fn, pools=10):
    tp = ThreadPoolExecutor(pools)
    def wrapper(*args):
        return tp.map(fn, *args)  # returns Future object
    return wrapper

class testit:
    def __init__(self, some_list: List[str]) -> None:
        self._ids = some_list
        print(self._ids)

    def test_first(self, some_id: List[int]) -> None:
        print(some_id)

class showit(testit):
    def __init__(self, *args):
        super(showit, self).__init__(*args)
    
    @threaded
    def again(self):
        global id_int
        for values in self.test_first(id_int):
            print(values)

a = showit(id_str)
print(a.again())

錯誤:

  File "test_1.py", line 32, in <module>
    print(a.again())
  File "test_1.py", line 10, in wrapper
    return tp.map(fn, *args)  # returns Future object
  File "/Users/usr/opt/anaconda3/lib/python3.8/concurrent/futures/_base.py", line 600, in map
    fs = [self.submit(fn, *args) for args in zip(*iterables)]
TypeError: 'showit' object is not iterable

預計 output:

1
2
3
4
5

注意事項:

  1. 編寫一個帶有可選參數的裝飾器需要與您完成它的方式有所不同。
  2. 我假設您希望threaded裝飾器能夠同時支持函數和方法,如果這不難實現的話。

修飾的函數/方法可以根據需要采用任意多個位置和關鍵字 arguments(但至少有一個位置參數)。 當您調用包裝的 function 時,最后一個位置參數必須是一個可迭代對象,它將與池的map方法一起使用。 所有位置和關鍵字 arguments 然后將在函數/方法作為 worker function 被map調用時傳遞給函數/方法,除了最后一個位置參數現在將是可迭代的元素。

from concurrent.futures import ThreadPoolExecutor
from functools import wraps, partial

def threaded(poolsize=10):
    def outer_wrapper(fn):
        wraps(fn)
        def wrapper(*args, **kwargs):
            """
            When the wrapper function is called,
            the last positional argument iterable will be an iterable
            to be used with the pool's map method.

            Then when map is invoking the original unwrapped function/method
            as a worker function, the last positional argument will be an element of
            that iterable.
            """

            # We are being called with an iterable as the first argument"
            # Construct a worker function if necessary:
            with ThreadPoolExecutor(poolsize) as executor:
                worker = partial(fn, *args[:-1], **kwargs)
                return list(executor.map(worker, args[-1]))
        return wrapper
    return outer_wrapper

iterable = [1, 2, 3]

@threaded(len(iterable))
def worker1(x, y, z=0):
    """
    y, the last positional argument, will be an iterable when the
    wrapper function is called or an element of the iterable
    when the actual, unwrapped function, worker1, is called.
    """
    return x + (y ** 2) - z

@threaded(3)
def worker2(x):
    return x.upper()

class MyClass:
    @threaded()
    def foo(self, x):
        return -x

# last positional argument is the iterable:
print(worker1(100, iterable, z=10))
print(worker2(['abcdef', 'ghijk', 'lmn']))
o = MyClass()
print(o.foo([3, 2, 1]))

印刷:

[91, 94, 99]
['ABCDEF', 'GHIJK', 'LMN']
[-3, -2, -1]

異常原因:

當您說a.again()時, Python 從 function again在您的 ZCAB142F2ED4F8EB402. 這是描述符的行為。

這意味着again的第一個參數將填充對新創建的showit object 的引用。

請參閱您在tp.map(fn, *args)行中調用的map方法的源代碼:

    def map(self, fn, *iterables, timeout=None, chunksize=1):
        ...
        ...
        fs = [self.submit(fn, *args) for args in zip(*iterables)]
        ...
        ...

Look at the interested line, it's going to unpack iterables which currently is tuple of single item which is the instance of the showit class and pass it to zip function, zip accepts iterables and your showit instance is not. 太確認將打印語句添加到wrapper function:

def threaded(fn, pools=10):
    tp = ThreadPoolExecutor(pools)

    def wrapper(*args):
        print(args)
        return tp.map(fn, *args)  # returns Future object

    return wrapper

如何解決?

其實你在做什么對我來說有點模糊。 我想也許你不需要concurrent.futures package 這里。 如果您的again方法應該在它自己的線程上運行,您可以執行以下操作:

import threading

id_str = ["1", "2", "3", "4", "5"]
id_int = [1, 2, 3, 4, 5]


def threaded(fn):
    def wrapper(self):
        threading.Thread(target=fn, args=(self,)).start()

    return wrapper


class testit:
    def __init__(self, some_list):
        self._ids = some_list

    def test_first(self, some_id: list[int]):
        return some_id


class showit(testit):
    @threaded
    def again(self):
        for values in self.test_first(id_int):
            print(values)


a = showit(id_str)
a.again()

另一個更新

雖然此答案中的代碼適用於上述問題中概述的特定情況,但請改用 Booboo 的答案,它更通用(並且教會了我很多關於裝飾器的知識)並且它還可以更好地處理線程池及其大小,因為現在所有線程都在裝飾器內部啟動,所以再次創建一個裝飾器私有線程池是有意義的——就像上面問題中概述的那樣。

更新答案!

根據你的評論,我相信這段代碼應該做,你想要什么(問題是, class 中的一個方法總是需要以 self 作為第一個參數來調用,並且也需要提供給 tp.map ) :

from concurrent.futures import ThreadPoolExecutor
from typing import List

id_str = ['1', '2', '3', '4', '5']
id_int = [1, 2, 3, 4, 5]
tp = ThreadPoolExecutor(10)

def threaded(fn):
    def wrapper(self,lst: List[int]):
        return tp.map(fn,[self]*len(lst),lst)
    return wrapper
    
class testit:
    def __init__(self, some_list: List[str]) -> None:
        self._ids = some_list
        print(self._ids)

    @threaded
    def test_first(self,some_id: int) -> int:
        return -some_id

class showit(testit):
    def __init__(self, *args):
        super(showit, self).__init__(*args)
    
    def again(self):
        global id_int
        for values in self.test_first(id_int):
            print(repr(values))

a = showit(id_str)
a.again()

但請注意:原始的 test_first 方法正在接收一個 int 作為參數,裝飾器正在將其轉換為 List[int] 並注意,為列表的每個元素調用原始的 test_first 方法並並行執行線程池 tp 中的線程。

您可以在https://onlinegdb.com/gJxvgniGQ查看該代碼的運行情況,它輸出:

['1', '2', '3', '4', '5']
-1
-2
-3
-4
-5

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM