Метод WinApi SpinLock для двух процессов - PullRequest
0 голосов
/ 06 августа 2020

У меня вопрос относительно обработки процесса Win32 API.

У меня два процесса. Процесс 1 перед запуском должен запустить процесс 2, который ожидает того же ресурса, что и процесс 1. Это файл .txt с некоторыми целочисленными значениями. Это означает, что Process1 нужно запустить ПЕРВЫМ и запустить Process2. Процесс Process1 ДОЛЖЕН быть завершен после Process2. Он должен работать следующим образом: 1. Создается процесс 1. 2. Процесс 1 заблокирован. 3. Процесс 2 создан и завершен. 4. Процесс 1 разблокирован и выполняется. 5. Процесс 2 завершается. 6. Процесс 1 завершается.

Я искал здесь вопрос, похожий на мой, я нашел только ссылку ниже, где представлен класс SpinLock:

C ++ 11 Реализация Spinlock с использованием

Проблема заключается в том, чтобы реализовать его правильно, я удалил свои неправильные реализации методов SpinLock из функции main().

Было практически невозможно найти какой-либо пример использования этого метода на практике, поэтому я задаю этот вопрос, чтобы взглянуть на него:

#include <iostream>
#include <Windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <pthread.h>
#include <atomic>
using namespace std;

class SpinLock {
    atomic_flag locked = ATOMIC_FLAG_INIT ;
public:
    void lock() {
        while (locked.test_and_set(memory_order_acquire)) { ; }
    }
    void unlock() {
        locked.clear(memory_order_release);
    }
};

int main( int argc, TCHAR *argv[] )
{
    //process 1 generates N random values between 1 and 100,then saves it to txt file i argv[1] stores quanity of values, which will be saved to file
    STARTUPINFO si = {};
    si.cb = sizeof si;
    SpinLock SpinLockVar;
   PROCESS_INFORMATION pi = {};
   const TCHAR* target1 = _T("C:\\USERS\\Admin\\Documents\\File1.exe"); //process 1
   const TCHAR* target2 = _T("C:\\USERS\\Admin\\Documents\\File2.exe");
   //Process 1 , before starting generating values and saving them to file, runs Process2,which is awaiting for access to txt file (SPINLOCK ).
   //Process 1 is terminating after finishing Process 2

   if ( !CreateProcess(target1,GetCommandLine(), 0, FALSE, 0, 0, 0, 0, &si, &pi) )
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        
        if ( PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ) // Good
            cout << "Request to terminate process has been sent!";

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
    if ( !CreateProcess(target2,0, 0, FALSE, 0, 0, 0, 0, &si, &pi) )
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        /*
        if ( TerminateProcess(pi.hProcess, 0) ) // Evil
            cout << "Process terminated!";
        */
        if ( PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ) // Good
            cout << "Request to terminate process has been sent!";

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }

    cin.sync();
    cin.ignore();
    
  

 
    return 0;
}

UPDATE Я использовал функцию мьютекса на данный момент, и он работает частично - у него есть механизм «спин-блокировки» мьютекса, хотя иногда он показывает очень странно. У меня случайным образом получается результат, который я ожидаю и который генерируется случайным образом после запуска моей программы - первая строка в cmd относится к потоку, который запускает process2, вторая строка является результатом process1

Пожалуйста, проверьте мой код:

#include <windows.h>
#include <stdio.h>
#include <pthread.h>
#include <tchar.h>
#include <mutex>
#include <iostream>
HANDLE hMutex;

DWORD ThreadProc1(LPVOID* arg)  
{  
       
      const TCHAR* target = _T("C:\\USERS\\Admin\\Documents\\File2.exe");
    PROCESS_INFORMATION pInfo;
    STARTUPINFO sInfo = { sizeof(pInfo) };
    BOOL res = CreateProcess(target, 0, 0, FALSE, 0, 0, 0, 0, &sInfo, &pInfo); //process2
    if (!res) return 1;
      
      WaitForSingleObject(pInfo.hThread, INFINITE);
    CloseHandle(pInfo.hThread);
    CloseHandle(pInfo.hProcess);
      return TRUE;  
}  

 
int main(void)  
{  
     PROCESS_INFORMATION pInfo;
    STARTUPINFO sInfo = { sizeof(pInfo) };
    const TCHAR* target = _T("C:\\USERS\\Admin\\Documents\\File1.exe");
      HANDLE hThreads;  
      DWORD threadID1; 

      
      hMutex=CreateMutex(NULL, FALSE, NULL); //create mutex(resources=1)
      WaitForSingleObject(hMutex, INFINITE); //process2 call WaitForSingleObject(hmutex) first to get mutex
      hThreads=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc1, &hMutex, 0, &threadID1); 
      WaitForSingleObject(hMutex,INFINITE);//process1 call WaitForSingleObject(hmutex) and block
      BOOL res = CreateProcess(target, GetCommandLine(), 0, FALSE, 0, 0, 0, 0, &sInfo, &pInfo);//process1
      if (!res) return 1;
      ReleaseMutex(hMutex);// process2 do file operations, and then release mutex
      WaitForSingleObject(hMutex,INFINITE);// process1 WaitForSingleObject(hmutex) unblock(resources -1),
      ReleaseMutex(hMutex);  // then release mutex(resources +1) 

      
      CloseHandle(hMutex);   
      WaitForSingleObject(hThreads,INFINITE); 
      CloseHandle(hThreads);    //process 1 closing thread after process 2 ends
     CloseHandle(pInfo.hProcess);
     CloseHandle(pInfo.hThread);
      return 0;
}  

1 Ответ

0 голосов
/ 10 августа 2020

Во-первых, я думаю, вам не нужно использовать мьютекс или спин-блокировку. Вы можете создать process1 с помощью CREATE_SUSPENDED, создать process2, дождаться завершения процесса process2, затем вызвать ResumeThread(pi1.hThread), хотя на шагах 4 и 5 могут быть различия.

#include <windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, TCHAR* argv[])
{

    STARTUPINFO si1 = { 0 }, si2 = { 0 };
    si1.cb = sizeof si1;
    si2.cb = sizeof si2;
    PROCESS_INFORMATION pi1 = { 0 }, pi2 = { 0 };

    const TCHAR* target1 = _T("C:\\Users\\drakew\\source\\repos\\Project4\\Debug\\Project4.exe");
    const TCHAR* target2 = _T("C:\\Users\\drakew\\source\\repos\\Project6\\Debug\\Project6.exe");


    if (!CreateProcess(target1, 0, 0, FALSE, 0, CREATE_SUSPENDED, 0, 0, &si1, &pi1))
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        printf("Process1 is created ...\n");
        printf("Process1 is blocked ...\n");
        if (!CreateProcess(target2, 0, 0, FALSE, 0, 0, 0, 0, &si2, &pi2))//Process 2 is created and excecuted ...
        {
            cerr << "CreateProcess failed (" << GetLastError() << ").\n";
        }
        else
        {
            WaitForSingleObject(pi2.hProcess, INFINITE);
            printf("Process 2 ends ...\n");
            CloseHandle(pi2.hProcess);
            CloseHandle(pi2.hThread);
        }
        ResumeThread(pi1.hThread); //Process 1 is unlocked and executed ...
        WaitForSingleObject(pi1.hProcess, INFINITE);
        printf("Process 1 ends ...\n");
        CloseHandle(pi1.hProcess);
        CloseHandle(pi1.hThread);
    }
    cin.sync();
    cin.ignore();

    return 0;
}

Затем, если вы хотите заблокировать в указанном c месте process1, вам нужно изменить process1 и process2, чтобы сохранить синхронизацию.

Я использую событие для реализации блокировки.

Процесс1:

#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
    HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE| SYNCHRONIZE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE| SYNCHRONIZE, FALSE, L"Global\\MyEvent2");
    printf("process1: step1 ...\n");
    SetEvent(hEvent1);
    printf("process1: step2 ...\n");
    DWORD dwWaitResult = WaitForSingleObject(hEvent2, INFINITE);
    switch (dwWaitResult)
    {
    case WAIT_OBJECT_0:
        printf("process1: step4 ...\n");
        break;
    default:
        return FALSE;
    }
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
}

Процесс2:

#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
    HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, L"Global\\MyEvent2");

    DWORD dwWaitResult = WaitForSingleObject(hEvent1, INFINITE);
    switch (dwWaitResult)
    {
    case WAIT_OBJECT_0:
        printf("process2: step3 ...\n");
        SetEvent(hEvent2);
        break;
    default:
        return FALSE;
    }
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
    return 1;
}

Основной процесс:

#include <windows.h>
#include <string>
#include <tchar.h>
#include <cstdlib>
#include <mutex>
#include <iostream>
using namespace std;
int main(int argc, TCHAR* argv[])
{

    STARTUPINFO si1 = { 0 }, si2 = { 0 };
    si1.cb = sizeof si1;
    si2.cb = sizeof si2;
    PROCESS_INFORMATION pi1 = { 0 }, pi2 = { 0 };
    mutex mtx;
    HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, L"Global\\MyEvent1");
    HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, L"Global\\MyEvent2");

    const TCHAR* target1 = _T("C:\\path\\process1.exe");
    const TCHAR* target2 = _T("C:\\path\\process2.exe");


    if (!CreateProcess(target1, 0, 0, FALSE, 0, 0, 0, 0, &si1, &pi1))
    {
        cerr << "CreateProcess failed (" << GetLastError() << ").\n";
    }
    else
    {
        if (!CreateProcess(target2, 0, 0, FALSE, 0, 0, 0, 0, &si2, &pi2))
        {
            cerr << "CreateProcess failed (" << GetLastError() << ").\n";
        }
        else
        {
            WaitForSingleObject(pi2.hProcess, INFINITE);
            printf("process2: step5 ...\n");
            CloseHandle(pi2.hProcess);
            CloseHandle(pi2.hThread);
        }

        WaitForSingleObject(pi1.hProcess, INFINITE);
        printf("process1: step6 ...\n");
        CloseHandle(pi1.hProcess);
        CloseHandle(pi1.hThread);
    }
    CloseHandle(hEvent1);
    CloseHandle(hEvent2);
    cin.sync();
    cin.ignore();
    return 0;
}

Результат:

введите описание изображения здесь

...