[英]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
時使用信號量來保護我的x
和y
。 但是當我調用my_class.x['a'] = 123
,不會調用my_class.__setattr__
。 所以我的x
不受保護。
我有兩個問題:
my_class.x['a'] = 123
時調用my_class.x['a'] = 123
哪個 python 函數?my_class
保護我的x
和y
,而不是全局list
和dict
; 我的x
和y
也可能有一個list
或一個dict
。更新:我想更新上面隨機代碼的一些概念。 我想創建一個類似內核的 AI。 AI必須同時做2個工作。 一是收集我提供的所有信息。 二是它必須在達到閾值時將信息保存到磁盤(我不希望它殺死我的 RAM)
我試圖做的
dict
和list
的class
,以覆蓋{}
和[]
,但它需要我更新所有{}
和[]
。 那不是效率。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.ChainMap
的self._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.