簡體   English   中英

python:python類中列表或字典上的信號量保護

[英]python: semaphore protection on a list or a dict in python class

我的代碼

class myclass:
    def __init__(self):
        self.x = {}
        self.y = []
        self.semaphore = threading.Semaphore()
    def __semaphore(func):
        def wrapper(**args. *kw):
            args[0].__sync_semaphore.acquire()
            ret = func(*args, **kw)
            args[0].__sync_semaphore.release()
            return ret
       return wrapper

    @__semaphore
    def __setattr__(self, name, value):
        super().__setattr__(name, value)

    @__semaphore
    def save_to_disk(self):
        """ access to my_class.x and my_class.y """

my_class = myclass()
my_class.x['a'] = 123

使用上面的代碼,我試圖在調用save_to_disk時使用信號量來保護我的xy 但是當我調用my_class.x['a'] = 123 ,不會調用my_class.__setattr__ 所以我的x不受保護。

我有兩個問題:

  • 當我調用my_class.x['a'] = 123時調用my_class.x['a'] = 123哪個 python 函數?
  • 如何僅在my_class保護我的xy ,而不是全局listdict 我的xy也可能有一個list或一個dict

更新:我想更新上面隨機代碼的一些概念。 我想創建一個類似內核的 AI。 AI必須同時做2個工作。 一是收集我提供的所有信息。 二是它必須在達到閾值時將信息保存到磁盤(我不希望它殺死我的 RAM)

我試圖做的

  • 創建一個繼承dictlistclass ,以覆蓋{}[] ,但它需要我更新所有{}[] 那不是效率。
  • 目前我正在嘗試創建一個讀/寫信號量,然后覆蓋dict().__setitem__list.append等。 但我不知道會發生什么

TLDR:僅在myclass方法上執行此操作是沒有用的,因為不僅涉及myclass my_class.x['a'] = 123相當於:

def set_x_a(obj: myclass, value):
    x = obj.__getattr__('x')   # fetch `x` via `myclass` method
    x.__setitem__('a', value)  # set `'a'` via `type(x)` method

set_x_a(my_class, 123)

請注意在調用x.__setitem__時對my_class.__getattr__的調用是如何完成的。 因此, my_class方法內部的任何同步都屬於錯誤的范圍。


您可以通過僅在同步塊中訪問類字段來保護類字段免受並發訪問。

Python 同步塊的基本方法是with語句,例如 可以與threading鎖一起使用 為了簡化創建自定義塊, contextlib.contextmanager使用單個生成器(而不是兩個方法)。 最后,property允許向屬性添加行為,例如同步。

import sys
import threading
from contextlib import contextmanager

class Synchronized:
    def __init__(self):
        self._x = {}  # actual data, stored internally
        self._mutex = threading.RLock()

    @property
    @contextmanager
    def x(self):           # public behaviour of data
        with self._mutex:  # only give access when synchronised
            yield self._x

    def save(self, file=sys.stdout):
        with self._mutex:  # only internally access when synchronised
            file.write(str(self._x))

重要的變化是dict屬性不再直接暴露。 它僅適用於持有鎖

synced = Synchronized()
with synced.x as x:
    x['a'] = 123
    x['b'] = 42

synced.save()

您可以將此模式擴展到其他屬性,並改進對屬性的保護。 例如,可以yield的復印件或collections.ChainMapself._x ,並且在該塊的結束明確地更新與此內部狀態-由此事后無效外部引用的效果。

第一季度

當我調用 my_class.x['a'] = 123 時調用了哪個 python 函數?

調用def __getattribute__(self, item):首先

關於你的想法

我想創建一個類似內核的 AI。 AI 必須同時做 2 個工作。 一是收集我提供的所有信息。 二是它必須在達到閾值時將信息保存到磁盤(我不希望它殺死我的 RAM)

問題是因為兩個線程想要共享同一個變量,對吧?

如果是這樣,也許您可​​以嘗試一次只讓一個線程工作,然后不必擔心資源會發生變化。

例如:

import threading
import numpy as np
from time import time, sleep


def get_data(share_list, share_dict):
    num_of_data = 0
    while num_of_data < 6:
        t_s = time()
        if is_writing_flag.is_set():
            sleep(REFRESH_TIME)
            continue

        while 1:
            data = np.random.normal(1, 1, (10,))
            threshold = all(data > 1.6)
            if threshold:
                share_list.append(data)
                share_dict['time'] = time() - t_s
                num_of_data += 1
                is_writing_flag.set()
                break
    close_keeper_flag.clear()


def data_keeper(share_list, share_dict):
    while close_keeper_flag.is_set():
        while is_writing_flag.is_set():
            # save as csv, json, yaml...
            print(share_list.pop())
            print(share_dict['time'])
            is_writing_flag.clear()
        sleep(REFRESH_TIME)


def main():
    share_list = []
    share_dict = {}
    td_collect_data = threading.Thread(target=get_data, name='collect some data', args=[share_list, share_dict])
    td_data_keeper = threading.Thread(target=data_keeper, name='save data.', args=[share_list, share_dict])
    for th in (td_collect_data, td_data_keeper):
        th.start()


if __name__ == '__main__':
    REFRESH_TIME = 0.2
    is_writing_flag = threading.Event()
    is_writing_flag.clear()

    close_keeper_flag = threading.Event()
    close_keeper_flag.set()
    main()

但是我更喜歡使用asyncio來處理這個,例如

import asyncio
import numpy as np
from time import time


async def take_data(num_of_data):
    count = 0
    t_s = time()
    while 1:
        if count == num_of_data:
            break
        data = await collect_data()
        cost_time = time() - t_s
        yield list(data), dict(time=cost_time)
        t_s = time()
        count += 1


async def collect_data():
    while 1:
        data = np.random.normal(1, 1, (10,))
        threshold = all(data > 1.6)
        if threshold:
            break
    return data


async def ai_process():
    async for res_list, res_dict in take_data(5):
        print(res_dict['time'])
        # save_to_desktop()
        ...


def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait([ai_process()]))
    loop.close()


if __name__ == '__main__':
    main()

如果這對您仍然沒有用處,我將刪除答案。 如果您有任何問題,請告訴我,謝謝。

暫無
暫無

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

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