Нет, безопасность резьбы может быть гарантирована только через замки.
Возможно ли, что self.cnt += 1
может выполняться дважды при многопоточности?
Если у вас есть два потока, выполняющих это действие, он будет выполнен дважды. Три потока, трижды и т. Д. Я не уверен, что вы на самом деле имеете в виду, возможно, покажите нам, как вы создаете/выполняете эти потоки по отношению к своему менеджеру контекста.
Возможно ли, что для того же экземпляра менеджера контекста, в многопоточности, как-то __enter__
можно назвать дважды и __exit__
вызывается только один раз, так что конечный результат self.cnt 1?
Да, конечный результат может быть отличным от нуля, но не через механизм, который вы принимаете с асимметричным вызовом входа и выхода. Если вы используете один и тот же экземпляр контекста менеджера по нескольким потокам, вы можете построить простой пример, который может воспроизводить ошибки следующим образом:
from threading import Thread
class Context(object):
def __init__(self):
self.cnt = 0
def __enter__(self):
self.cnt += 1
def __exit__(self, exc_type, exc_value, traceback):
self.cnt -= 1
shared_context = Context()
def run(thread_id):
with shared_context:
print('enter: shared_context.cnt = %d, thread_id = %d' % (
shared_context.cnt, thread_id))
print('exit: shared_context.cnt = %d, thread_id = %d' % (
shared_context.cnt, thread_id))
threads = [Thread(target=run, args=(i,)) for i in range(1000)]
# Start all threads
for t in threads:
t.start()
# Wait for all threads to finish before printing the final cnt
for t in threads:
t.join()
print(shared_context.cnt)
Вы неизбежно обнаружит, что окончательное shared_context.cnt
часто не в конечном итоге обратно в 0
, даже хотя, когда все нити начали и закончили с точно таким же кодом, даже если входом и выходом все называют более или менее пары:
enter: shared_context.cnt = 3, thread_id = 998
exit: shared_context.cnt = 3, thread_id = 998
enter: shared_context.cnt = 3, thread_id = 999
exit: shared_context.cnt = 3, thread_id = 999
2
...
enter: shared_context.cnt = 0, thread_id = 998
exit: shared_context.cnt = 0, thread_id = 998
enter: shared_context.cnt = 1, thread_id = 999
exit: shared_context.cnt = 0, thread_id = 999
-1
Это главным образом вызван оператором +=
решается четыре опкодов и гарантируются только индивидуальные коды операций чтобы быть в безопасности, если только GIL. Более подробную информацию можно найти по этому вопросу: Is the += operator thread-safe in Python?
Что вы подразумеваете под тем же экземпляром в многопоточном режиме? Независимо, если вы даете каждому потоку собственный экземпляр любого контекстного менеджера, который вы, возможно, используете, это не будет иметь отношения к делу. Если доступ к данным осуществляется совместно между потоками, доступ к ним должен управляться с помощью мьютексов/блокировок. – metatoaster
@metatoaster Я согласен, каждый поток имеет свой собственный contextmanager, но мой коллега говорит, что '__enter__' - это всего лишь метод экземпляра, он может быть вызван дважды, поэтому он не является потокобезопасным, поэтому тот же самый экземпляр экземпляра contextmanager' __enter__' не является потоком безопасно. Я чувствую себя потерянным – est
Если каждый поток имеет свой собственный экземпляр, тогда нет общих данных. – martineau