mpi4py и ctypes — программа на Python с MPI завершается при выходе из одного потока (через C )

#python #c #ctypes #mpi4py

#python #c #ctypes #mpi4py

Вопрос:

(Используемые здесь коды аналогичны другому вопросу, который я опубликовал сегодня, но два вопроса разные)

Я использую сложный код на C с интерфейсом Python3 (с использованием ctypes). Для простоты я скопировал простой код C ниже, чтобы сформулировать свой вопрос. Функция exitOrNot() принимает аргумент index и завершает работу, когда он равен 7.

multi.cpp

 #include <iostream> 
#include <cstdlib>

extern "C" int exitOrNot(int index) {
    int ret;
    if(index == 7) {
        std::cout << "EXITING... n";
        std::exit(EXIT_FAILURE);
    }
    else {
        sleep(1)
        ret = index;
    }
    return ret;
}
 

Я создаю общую библиотеку для вышеупомянутой программы libmulti.so используя следующую команду — g -fPIC -shared -o libmulti.so multi.cpp

В моем модуле Python я использую mpi4py для параллельной обработки. В этом простом примере у меня есть две функции — ctypes_exitOrNot() для получения функции C из предыдущей и multi() для перебора множества входных аргументов (в этом примере 5 * 100 = 500 аргументов).

multi.py

 import numpy as np
from mpi4py import MPI
import ctypes

_multi = ctypes.CDLL('./libmulti.so')

def ctypes_exitOrNot(index):
    ret = _multi.exitOrNot(ctypes.c_int(index))
    return ret

def multi(N):
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()
    N_threads = comm.Get_size()-1 # Thread 0 is used only for IO
    
    N_jobs = N
    N_jobs_per_thread = int(np.ceil( float(N_jobs)/float(N_threads) ))
        
    print("nrank",rank,"N_threads",N_threads,"N_jobs_per_thread",N_jobs_per_thread,"n")

    indices = range(rank-1,N_jobs,N_threads)
    
    if rank == 0: # IO thread             
        completed_ranks = []
        while True: # Wait to receive data until all threads are done
            status = MPI.Status()
            data = comm.recv(source=MPI.ANY_SOURCE,tag=MPI.ANY_TAG,status=status)
            received_rank = status.Get_source()
            
            if data == "done":
                completed_ranks.append(received_rank)
            else:               
                index_system = data["index_system"]    
                with open("completed.txt","a") as file_completed:
                    file_completed.write(str(index_system)   'n')
                
            if len(completed_ranks)==N_threads:
                print("nrank 0 -- all threads done; completed_ranks",completed_ranks,"n")
                break

    else:
        for index_rank,index_job in enumerate(indices):
            index_system = index_job
            
            print("rank",rank,"index_system",index_system,"started")

            # EXITING CONDITION FROM EXTENAL C  
            ret = ctypes_exitOrNot(index_system)
            data = {"index_system":ret}

            print("rank",rank,"index_system",index_system,"ended")
            
            comm.send(data, dest=0, tag=index_system)
        
        # This thread is done
        data = "done"
        comm.send(data, dest=0, tag=index_system)
    
if __name__ == '__main__':
    multi(5*100)
 

Важная строка приведенного выше кода ret = ctypes_exitOrNot(index_system) . Целочисленный аргумент, передаваемый в exitOrNot(), имеет индекс 0-499, и когда они разделяются на разные потоки, поток с индексом 7 завершается. Для справки, моя команда терминала — ‘mpiexec -n 6 python3 multi.py ‘

Я, наконец, перехожу к своему вопросу. Когда поток завершается через C (как в этом случае), вся программа завершается, т.Е. Все остальные потоки также завершаются. Однако, когда тот же самый выход выполняется из Python, другие потоки продолжают выполняться.

Есть ли какой-либо способ, чтобы другие потоки продолжали работать, даже когда один из них завершается через C ? (Я не могу редактировать сам фактический файл C )

Комментарии:

1. std::exit завершает весь процесс. Неважно, как он вызывается, он завершает весь процесс. Возможно, среда python использует отдельные процессы, а не потоки выполнения.

2. Предполагая, что «выход из Python» означает sys.exit() sys.exit() выход только из потока, который его вызвал, и процесс Python завершается только в том случае, если все остальные потоки являются потоками демона. Не то же std::exit() самое, что в C .

3. Есть ли команда, чтобы не завершать весь процесс через C ? Выход только из одного потока вместо этого?