2017-08-21 40 views
1

ここにシナリオがあります。メインコードを実行する前に別のプログラム(Pと呼ぶ)を起動するPythonスクリプトがあります。複数のPythonスクリプトは、プログラムPがすでに開始されている場合しかし、それが再び開始すべきではない、同時に実行することができます。Windows上のPythonと共有メモリのセマフォ?

CIで
pid 100 starts up 
pid 100 starts program P with pid n and detaches it 
pid 100 runs main 
pid 101 starts up 
pid 101 does not start program P because it's already started 
pid 101 runs main 
pid 100 main finishes 
pid 100 does not terminate program P because pid 101 needs it 
pid 100 terminates 
pid 101 main finishes 
pid 101 terminates program P with pid n 
pid 101 terminates 

はおそらくファイルをINGのパッティングmmapで共有メモリを作成することができますそれを追跡するためのセマフォー。セマフォーが0に達すると、プログラムPを終了することもできます。しかし、私はWindows上のPython でこれを行う方法は知られていません。私はつまり、この問題にアプローチする必要がありどのように

は、これを解決するためのすでに確立された方法はありますか?

+0

あなたは[ 'mmap']を使用することができます(https://docs.python.org/3/library/mmap.html )モジュールを 'tagname'で置き換えます。 Windowsの共有メモリはセクションオブジェクトであり、他のいくつかのカーネルオブジェクトタイプ(デバイス、イベントなど)のようにオブジェクト名前空間に名前を付けることができます。 「share1234」などの名前は、現在のWindowsセッションのローカルです。すべてのセッションで名前にアクセスできるようにするには、 "Global \ share1234"を使用します。後者は、区切り記号としてバックスラッシュを使用しなければなりません。スラッシュはカーネル内の名前文字に過ぎないからです。 – eryksun

+0

グローバル名を作成すると、これがどのように実装されているかによってパス区切りが使用されます。 Windows APIは、グローバル名(すべてのセッション)に '\ BaseNamedObjects'を、セッションにローカルな名前として' \ Sessions \ [セッション番号] \ BaseNamedObjects'を使います。ローカルのBNOには "グローバル"なシンボリックリンクがありますので、これは実際には '\ Sessions \ 1 \ BaseNamedObjects \ Global \ share1234' =>' \ BaseNamedObjects \ share1234'のように解決しています。 – eryksun

答えて

1

私はあなたがソケットではなく、共有メモリセマフォを使用してWindows上でこの動作を実現することができる方法の例を作成しました。これは同じことを成し遂げます。少なくとも1つのPythonスクリプトが実行されている限り、C++プログラムは動作し続けます。すべてのスクリプトが終了すると、特定のタイムアウト時間内にPythonスクリプトが起動しなくなると、C++プログラムは終了します。

ここでのコードのほとんどは、Pythonスクリプト(複数可)からのTCP接続を監視するスレッドを実行し、C++プログラム、になります。

ザ・Pythonスクリプトだけチェック/ Windowsプログラムを開始し、その後、スクリプトが終了するまで開いたままにされたソケットを開きます。

Windowsプログラムは、このようにPythonスクリプトが実行されているときを追跡し、ソケット接続と切断を検出しました。これらの例では

、Windowsプログラムは、「ConsoleApplication11.exe」と呼ばれるように起こります。私はポート1234と15秒のタイムアウトを使用しました。これらは、C++プログラムの19行目から21行目で変更できます。また、C++プログラムの終了をより即座にしたい場合は、client_monitor_thread()の最後にreturnの代わりにexit()を呼び出します。

これは役に立ちます。

の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++プログラム:

// 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; 
} 
+0

これは興味深い考えですが、並列プログラミングでは安全ではありません。 (別のインスタンスが 'progRunning'の前に起動してプログラムを2回起動するかもしれません)私は実際に[mutex](https://gist.github.com/derek1906/fab1773852362d45e024bacc239dcaf7)を作成して問題を解決することができました。 Windowsファイルロックを使用して異なるプロセス間で使用することができます。 –

+0

それにもかかわらず、並列プログラミングにとっては安全です。設計上、C++プログラムの2つのインスタンスは、両方が同じポートでリッスンできないため実行できず、1つは失敗して終了します。しかし、あなたのミューテックス解決策でうまくいっています。 –

関連する問題