Зависание потока в python
При запуске кода в 2 потока(1-ый сканирует папку на наличие *dcm файлов и добавляет информацию в бд, 2-ой поток занимается копированием этих файлов в другую папку). Но проблема в том, что при выполнении 2 потока программа зависает и не происходит никаких действий.
def create_bd():
connect_db = sqlite3.connect("study.sqlite")
try:
with closing(connect_db) as db:
with open('screening_.sql', encoding="utf8") as f:
db.cursor().executescript(f.read())
db.commit()
except sqlite3.OperationalError:
pass
def anonymization_image(path_load, path_save):
dcm = dicomio_read_file(path_load)
list_tags = ['PatientName','PatientBirthTime','InstitutionAddress',
'PatientDeathDateInAlternativeCalendar', 'PatientComments',
'PersonAddress', 'InstitutionName', 'ResponsiblePerson',
'AdmissionID', 'DeviceSerialNumber', 'DeviceDescription']
if hasattr(dcm, 'PatientAge'):
dcm.PatientAge = '0000Y'
if hasattr(dcm, 'PatientBirthDate'):
dcm.PatientBirthDate = "19000101"
for tag in list_tags:
if hasattr(dcm, tag):
dcm.tag = '*anonymized*'
dcm.save_as(path_save)
def get_dcm_tags(path):
modality = None
gender = None
study_description = None
studydate = None
dcm = dicomio_read_file(path)
if hasattr(dcm, 'Modality'):
modality = dcm.Modality
if hasattr(dcm, 'PatientSex'):
gender = dcm.PatientSex
if hasattr(dcm, 'StudyDescription'):
study_description = dcm.StudyDescription
if hasattr(dcm, 'StudyTime'):
studydate = dcm.StudyTime
return dcm.PatientID, dcm.StudyInstanceUID, dcm.SeriesInstanceUID, gender, modality, study_description, studydate
def scaning_folder(folder, queue, event):
"""
Собирает в указанной директории информацию
о снимках и заносит данные в бд
"""
while not event.is_set():
for dir_path, _, filenames in os.walk(folder):
for file_name in filenames:
if fnmatch.fnmatch(file_name, "*.dcm"):
path_file = f'{dir_path}/{file_name}'
# Заносим информацию в бд
append_study_in_bd(path_file)
# Отправляем данные во второй поток
queue.put(path_file)
def append_study_in_bd(path):
patientid, study, series, gender, modality, study_description, studydate = get_dcm_tags(path)
Session = sessionmaker(bind=engine)
session = Session()
patient = PatientClass(patientid, gender)
try:
if PatientClass.check_patient(patientID=patientid):
session.add(patient)
session.flush()
query_study = session.query(PatientClass).filter_by(patientID=patientid).first()
studies = StudyClass(study, query_study.id, studydate, study_description)
if StudyClass.check_study(studyUID=study):
session.add(studies)
session.flush()
query = session.query(StudyClass).filter_by(studyUID=study).first()
seriess = SeriesClass(query.id, series, modality, path)
if SeriesClass.check_series(seriesUID=series):
session.add(seriess)
session.commit()
except exc.IntegrityError:
session.rollback()
print("Произошла ошибка при добавление в бд")
def transfer_data(output_path, queue, event):
while not event.is_set() or not queue.empty():
path = queue.get()
folder = path.replace("\\",'/')
folder_series = folder.split('/')[-3]
folder_imgs = folder.split('/')[-2]
file_name = folder.split('/')[-1]
try:
os.makedirs(f"{output_path}/{folder_series}/{folder_imgs}")
except FileExistsError:
pass
anonymization_image(folder, f"{output_path}/{folder_series}/{folder_imgs}/{file_name}")
if __name__ == '__main__':
input_path = "C:/Users/Nikolau/Desktop/INFORMATION_ABOUT_STUDY/test"
output_path = "data"
create_bd()
pipeline = queue.Queue(maxsize=10)
event = threading.Event()
with ThreadPoolExecutor(max_workers=2) as executor:
executor.submit(scaning_folder, input_path, pipeline, event)
executor.submit(transfer_data, output_path, pipeline, event)
time.sleep(0.1)
event.set()
Ответы (2 шт):
Попробуйте данным способом
import threading
#### для создания очереди
LOCK = threading.Lock()
### запретить доступ к файлу другим потокам
# LOCK.acquire()
# разрешить доступ к файлу другим потокам
# LOCK.release()
def create_bd():
connect_db = sqlite3.connect("study.sqlite")
try:
LOCK.acquire()
with closing(connect_db) as db:
with open('screening_.sql', encoding="utf8") as f:
db.cursor().executescript(f.read())
db.commit()
LOCK.release()
except:
LOCK.release()
def anonymization_image(path_load, path_save):
dcm = dicomio_read_file(path_load)
list_tags = ['PatientName', 'PatientBirthTime', 'InstitutionAddress',
'PatientDeathDateInAlternativeCalendar', 'PatientComments',
'PersonAddress', 'InstitutionName', 'ResponsiblePerson',
'AdmissionID', 'DeviceSerialNumber', 'DeviceDescription']
if hasattr(dcm, 'PatientAge'):
dcm.PatientAge = '0000Y'
if hasattr(dcm, 'PatientBirthDate'):
dcm.PatientBirthDate = "19000101"
for tag in list_tags:
if hasattr(dcm, tag):
dcm.tag = '*anonymized*'
dcm.save_as(path_save)
def get_dcm_tags(path):
modality = None
gender = None
study_description = None
studydate = None
dcm = dicomio_read_file(path)
if hasattr(dcm, 'Modality'):
modality = dcm.Modality
if hasattr(dcm, 'PatientSex'):
gender = dcm.PatientSex
if hasattr(dcm, 'StudyDescription'):
study_description = dcm.StudyDescription
if hasattr(dcm, 'StudyTime'):
studydate = dcm.StudyTime
return dcm.PatientID, dcm.StudyInstanceUID, dcm.SeriesInstanceUID, gender, modality, study_description, studydate
def scaning_folder(folder, queue, event):
"""
Собирает в указанной директории информацию
о снимках и заносит данные в бд
"""
while not event.is_set():
for dir_path, _, filenames in os.walk(folder):
for file_name in filenames:
if fnmatch.fnmatch(file_name, "*.dcm"):
path_file = f'{dir_path}/{file_name}'
# Заносим информацию в бд
append_study_in_bd(path_file)
# Отправляем данные во второй поток
queue.put(path_file)
def append_study_in_bd(path):
patientid, study, series, gender, modality, study_description, studydate =
get_dcm_tags(
path)
Session = sessionmaker(bind=engine)
session = Session()
patient = PatientClass(patientid, gender)
try:
if PatientClass.check_patient(patientID=patientid):
session.add(patient)
session.flush()
query_study = session.query(PatientClass).filter_by(
patientID=patientid).first()
studies = StudyClass(study, query_study.id, studydate,
study_description)
if StudyClass.check_study(studyUID=study):
session.add(studies)
session.flush()
query = session.query(StudyClass).filter_by(studyUID=study).first()
seriess = SeriesClass(query.id, series, modality, path)
if SeriesClass.check_series(seriesUID=series):
session.add(seriess)
session.commit()
except exc.IntegrityError:
session.rollback()
print("Произошла ошибка при добавление в бд")
def transfer_data(output_path, queue, event):
while not event.is_set() or not queue.empty():
path = queue.get()
folder = path.replace("\\", '/')
folder_series = folder.split('/')[-3]
folder_imgs = folder.split('/')[-2]
file_name = folder.split('/')[-1]
try:
os.makedirs(f"{output_path}/{folder_series}/{folder_imgs}")
except FileExistsError:
pass
anonymization_image(folder,
f"{output_path}/{folder_series}/{folder_imgs}/{file_name}")
if __name__ == '__main__':
input_path = "C:/Users/Nikolau/Desktop/INFORMATION_ABOUT_STUDY/test"
output_path = "data"
create_bd()
pipeline = queue.Queue(maxsize=10)
event = threading.Event()
threadList = []
thread_1 = threading.Thread(target='Функция', args=['Аргументы'])
thread_2 = threading.Thread(target='Функция', args=['Аргументы'])
threadList.append(thread_1)
thread_1.start()
threadList.append(thread_2)
thread_2.start()
import time
while threading.active_count() > 2:
time.sleep(1)
for thread in threadList:
thread.join()
Вам нужно будет дополнить доступ в остальных случаях.
В программе есть "гонки", т.е. ее выполнение зависит от того, какие потоки и в каком порядке будут выполнятся.
Главная проблема - это то, что через 100 мс после запуска потоков устанавливается event, который интерпретируется потоками, как запрос на остановку. Поэтому, вполне вероятна следующая последовательность событий:
- главный поток запускает два рабочих потока
scaning_folderдобавляет элемент (или элементы) в очередьtransfer_dataдостает и обрабатывает элементы- взводится
event - в какой-то момент
tranfer_dataзаканчивает обрабатывать все доступные на данный момент элементы в очереди tranfer_dataидет на новый виток цикла и тут получается, что очередь пуста иevent.is_set- обработка заканчивается и поток выходит. При этомscaning_folderпродолжит выполнение, как только придет его очередь.
Исправить можно по разному. В первую очередь нужно изменить условие ожидания и саму логику остановки программы. Вероятно, она должна останавливаться, когда закончит обрабатывать все файлы. Т.е. event нужно взводить не из главного потока, а из scaning_folder. А главный поток, должен ждать, когда закончится transfer_data.