J'ai créé un exemple de la façon dont vous pouvez obtenir ce comportement sous Windows en utilisant des sockets plutôt qu'un sémaphore à mémoire partagée. Cela réalise la même chose; le programme C++ continuera à fonctionner tant qu'au moins un des scripts Python est en cours d'exécution. Une fois tous les scripts terminés, le programme C++ se termine tant qu'aucun autre script Python n'est démarré pendant une certaine période de temps.
La plupart du code va dans le programme C++, qui exécute un thread qui surveille les connexions TCP à partir du (des) script (s) Python.
Le script Python vérifie/démarre le programme Windows, puis ouvre un socket qui reste ouvert jusqu'à la fin du script.
Le programme Windows détecte les connexions et les déconnexions des sockets, en gardant une trace du moment où les scripts Python sont en cours d'exécution.
Dans ces exemples, le programme Windows est appelé "ConsoleApplication11.exe". J'ai utilisé le port 1234 et un délai de 15 secondes, vous pouvez les modifier dans les lignes 19-21 du programme C++. En outre, si vous voulez rendre la fin du programme C++ plus immédiate, appelez exit() au lieu de return à la fin de client_monitor_thread().
Espérons que cela puisse vous être utile.
script Python:
import socket
import time
import psutil
import os
# check if the C++ program is running, start it if not
cppProgramName = "ConsoleApplication11.exe"
progRunning = False
for pid in psutil.pids():
p = psutil.Process(pid)
if (cppProgramName in p.name()):
progRunning = True
if not progRunning:
os.startfile(cppProgramName)
time.sleep(5) # wait for c++ program to start
# open a socket to the C++ program to tell it we need it to keep running
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 1234))
# (MAIN PROGRAM)
time.sleep(3)
# (END OF MAIN PROGRAM)
# close the socket to the C++ program
s.close()
C++ programme:
// ConsoleApplication11.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <set>
#include <chrono>
#include <thread>
#include <winsock2.h>
#include <Ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib") // link with Ws2_32.lib
namespace
{
const unsigned int commonPort = 1234; // must match Python app
const unsigned int noClientsTimeoutLimit = 15; // quit when no clients connected for this many seconds
bool clientMonitorRunning = true; // flag to show client monitor is running
void client_monitor_thread()
{
// start up winsock service version 2.2
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
{
std::cout << "WSAStartup() failed with error: " << iResult << std::endl;
clientMonitorRunning = false;
return;
}
// create a socket for listening for incoming connection requests.
SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket == INVALID_SOCKET)
{
std::cout << "socket() function failed with error: " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
clientMonitorRunning = false;
return;
}
// sockaddr_in structure specifies the address family, IP address, and port for the socket
sockaddr_in service;
service.sin_family = AF_INET;
inet_pton(AF_INET, (PCSTR)"127.0.0.1", &(service.sin_addr));
service.sin_port = htons(commonPort);
if (SOCKET_ERROR == bind(listenSocket, (SOCKADDR *)& service, sizeof(service)))
{
std::cout << "bind function failed with error " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
clientMonitorRunning = false;
return;
}
// Listen for incoming connection requests on the created socket
if (SOCKET_ERROR == listen(listenSocket, SOMAXCONN))
{
wprintf(L"listen function failed with error: %d\n", WSAGetLastError());
closesocket(listenSocket);
clientMonitorRunning = false;
return;
}
std::cout << "Listening on port " << commonPort << std::endl;
// mow monitor client connections
std::set<unsigned int> activefds;
int timeoutCounter = 0;
while (clientMonitorRunning)
{
// check for existing clients disconnected
if (0 != activefds.size())
{
std::set<unsigned int> disconnectedfds;
for (auto fd : activefds)
{
int flags = 0;
char buf[10];
int rv = recv(fd, buf, 10, flags);
if (0 == rv)
{
disconnectedfds.insert(fd);
}
}
for (auto fd : disconnectedfds)
{
activefds.erase(fd);
}
}
// are any clients connected? do we need to quit?
if (0 == activefds.size())
{
std::cout << "No clients - will exit in " << noClientsTimeoutLimit - timeoutCounter << " seconds" << std::endl;
++timeoutCounter;
if (timeoutCounter == noClientsTimeoutLimit)
{
for (auto fd : activefds)
{
closesocket(fd);
}
closesocket(listenSocket);
clientMonitorRunning = false;
return;
}
}
else
{
timeoutCounter = 0;
}
// check for activity on the listening socket
fd_set readfds;
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(listenSocket, &readfds);
switch (select(sizeof(readfds), &readfds, NULL, NULL, &timeout))
{
case 0: // timeout
{
break;
}
case SOCKET_ERROR:
{
std::cout << "listen failed with error: " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
clientMonitorRunning = false;
return;
}
default:
{
if (FD_ISSET(listenSocket, &readfds))
{
// accept the connection.
SOCKET fd = accept(listenSocket, NULL, NULL);
if (fd == INVALID_SOCKET)
{
std::cout << "accept failed with error: " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
clientMonitorRunning = false;
return;
}
else
{
unsigned long nonBlock = 1;
ioctlsocket(fd, FIONBIO, &nonBlock);
activefds.insert(fd);
}
}
break;
}
}
}
return;
}
}
int main()
{
// start the client monitor thread, which will run until no clients are connected
std::thread clientMonitor(client_monitor_thread);
// placeholder for doing the actual work in this program
// loop until the client monitor thread exits
while (clientMonitorRunning)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// clean up
clientMonitor.join();
WSACleanup();
return 0;
}
Vous pouvez utiliser les touches [ 'mmap'] (https://docs.python.org/3/library/mmap.html) module avec un 'tagname'. La mémoire partagée dans Windows est un objet Section qui, comme plusieurs autres types d'objets noyau (par exemple, Périphériques, Événements) peut être nommé dans l'espace de noms de l'objet. Un nom tel que "share1234" est local à la session Windows en cours. Pour que le nom soit accessible à toutes les sessions, utilisez "Global \ share1234". Ce dernier doit utiliser la barre oblique inversée comme séparateur puisque la barre oblique est juste un caractère de nom dans le noyau. – eryksun
La création d'un nom global utilise un séparateur de chemin en raison de la manière dont il est implémenté. L'API Windows utilise '\ BaseNamedObjects' pour ses noms globaux (toutes les sessions) et' \ Sessions \ [numéro de session] \ BaseNamedObjects' pour les noms locaux à une session. Il y a un lien symbolique "Global" dans le BNO local, donc cela résout vraiment cela comme, par exemple, '\ Sessions \ 1 \ BaseNamedObjects \ Global \ share1234' =>' \ BaseNamedObjects \ share1234'. – eryksun