Windows信号量

时间:2017-11-14 16:05:44

标签: c++ windows winapi process semaphore

我需要实现一个使用信号量将打开的记事本窗口数量限制为10的程序。我在WM_INITDIALOG中创建了一个信号量

    semafor = CreateSemaphore(0, 1, 10, "semaphore");

每次我点击按钮打开一个新窗口,我打开那个信号量。然而,它并没有阻止我打开超过10个窗口。

当我点击对话框中的按钮打开一个新窗口时,这是我的代码:

case WM_COMMAND:
    switch (LOWORD(wParam)) {
        case ID_OK: 

            semafor = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, "semaphore");
            if (semafor == NULL) {
                printf("Eroare deschidere semafor empty: %d \n", GetLastError());
                ExitProcess(1);
            }

            BOOL b = CreateProcess("C:\\Windows\\System32\\notepad.exe",
                NULL, NULL, NULL, TRUE, 0, NULL, NULL,
                &si, &pi);

            process[++i] = GetCurrentProcess();

            if (b) {
                dwWaitForChild = WaitForInputIdle(pi.hProcess, 2000);
                switch (dwWaitForChild) {
                case 0:
                    printf("Procesul fiu este ready!\n");
                    break;
                case WAIT_TIMEOUT:
                    printf("Au trecut 2 sec. si procesul fiu nu este ready!\n");
                    break;
                case 0xFFFFFFFF:
                    printf("Eroare!\n");
                    break;
                }

                WaitForMultipleObjects(i, process, TRUE, INFINITE);

                iRasp = MessageBox(NULL, "Terminam procesul fiu?", "Atentie!", MB_YESNO);
                if (iRasp == IDYES) {
                    if (TerminateProcess(pi.hProcess, 2)) {

                        DWORD dwP;

                        GetExitCodeProcess(pi.hProcess, &dwP);
                        printf("Codul de terminare al procesului fiu: %d\n", dwP);
                        ReleaseSemaphore(semafor, 1, NULL);
                        CloseHandle(pi.hProcess);
                        printf("\nProcesul fiu a fost terminat cu succes\n");
                    }
                    else {
                        //tiparim mesajul de eroare 
                        TCHAR buffer[80];
                        LPVOID lpMsgBuf;
                        DWORD dw = GetLastError();

                        FormatMessage(
                            FORMAT_MESSAGE_ALLOCATE_BUFFER |
                            FORMAT_MESSAGE_FROM_SYSTEM,
                            NULL,
                            dw,
                            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                            (LPTSTR)&lpMsgBuf,
                            0, NULL);

                        wsprintf(buffer, "TerminateProcess() a esuat cu eroarea %d: %s",
                            dw, lpMsgBuf);

                        MessageBox(NULL, buffer, "Eroare!", MB_OK);

                        LocalFree(lpMsgBuf);
                    }
                } // rasp YES

            }
            else
                printf("Eroare la crearea procesului fiu!\n");

            return TRUE;
        }
        break;
    }
    return FALSE;
}

2 个答案:

答案 0 :(得分:2)

你不会在信号量上等待(或信号量逻辑中的P)。

检查winapi example如何使用信号量。在他们的示例中,他们使用WaitForSingleObject。你只使用OpenSemaphore只给你信号量句柄,这样你就可以在多个进程中使用它,如果你想减少它的值,那么你需要实际等待它,并在它为0时等待/超时。

在打开信号量之后和尝试打开记事本实例之前,你应该做这样的事情:

// Try to enter the semaphore gate.

dwWaitResult = WaitForSingleObject( 
    ghSemaphore,   // handle to semaphore
    0L);           // zero-second time-out interval

答案 1 :(得分:0)

你滥用信号量。一次创建信号量并重复使用它,不要一遍又一遍地重新打开它。但是,更重要的是,您必须等待信号量,例如使用WaitForSingleObject(),减少其计数器,然后使用ReleaseSeamphore()释放它以增加其计数器。信号量的状态在其计数器大于0时发出信号,在0时没有信号。

因此,您需要创建初始计数为10的信号量,然后在每次要创建新进程时等待它。如果等待失败,则已经有太多进程在运行。否则,如果等待成功,则计数器已递减,因此创建新进程,然后在进程结束时递增计数器。

然而,话虽如此,在您展示的代码的上下文中,您的信号量是完全无用的。您希望使用信号量来控制记事本的运行实例数,但是一旦启动1个实例,您就会尝试(错误地,我可能会添加)来阻止您的代码,直到该实例退出。所以你永远不能一次运行多个实例,使信号量无用。您可以运行超过1的事实可能是由于代码中的错误(您正在等待错误的进程句柄!)。不要使用阻塞等待。让系统在每个衍生过程结束时通知您。

在代码中使用信号量的正确方法看起来更像是这样:

const UINT WM_PROCESS_ENDED = WM_APP + 1;
const int MAX_PROCESSES = 10;

typedef struct procInfo
{
    HANDLE hProcess;
    HANDLE hWait;
} procInfo;

HANDLE semafor = NULL;
procInfo process[MAX_PROCESSES] = {};
int numProcesses = 0;
HWND hwndDialog;

...

VOID CALLBACK ProcessEnded(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
    PostMessage(hwndDialog, WM_PROCESS_ENDED, 0, (LPARAM)lpParameter);
}

...

case WM_INITDIALOG:
{
    hwndDialog = hwnd;

    semafor = CreateSemaphore(0, MAX_PROCESSES, MAX_PROCESSES, NULL);
    if (semafor == NULL)
    {
        printf("Eroare deschidere semafor empty: %d \n", GetLastError());
        ExitProcess(1);
    }

    break;
}

case WM_DESTROY:
{
    for (int i = 0; i < numProcesses; ++i)
    {
        UnregisterWaitEx(process[i].hWait, INVALID_HANDLE_VALUE);
        TerminateProcess(process[i].hProcess, 2);
        CloseHandle(process[i].hProcess);
    }

    CloseHandle(semafor);
    semafor = NULL;

    hwndDialog = NULL;

    break;
}

case WM_COMMAND:
    switch (LOWORD(wParam))
    {
        case ID_OK:
        {
            if (WaitForSingleObject(semafor, 0) != WAIT_OBJECT_0)
            {
                // too many instances running...
                break;
            }

            if (!CreateProcess("C:\\Windows\\System32\\notepad.exe",
                NULL, NULL, NULL, TRUE, 0, NULL, NULL,
                &si, &pi))
            {
                printf("Eroare la crearea procesului fiu!\n");
                ReleaseSemaphore(semafor, 1, NULL);
                break;
            }

            CloseHandle(pi.hThread);

            dwWaitForChild = WaitForInputIdle(pi.hProcess, 2000);
            switch (dwWaitForChild)
            {
                case 0:
                    printf("Procesul fiu este ready!\n");
                    break;

                case WAIT_TIMEOUT:
                    printf("Au trecut 2 sec. si procesul fiu nu este ready!\n");
                    break;

                case WAIT_FAILED:
                    printf("Eroare!\n");
                    break;
            }

            procInfo info;

            info.hProcess = pi.hProcess;
            if (!RegisterWaitForSingleObject(&info.hWait, pi.hProcess, &ProcessEnded, pi.hProcess, INFINITE, WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE))
            {
                TerminateProcess(pi.hProcess, 2);
                ReleaseSemaphore(semafor, 1, NULL);
                break;
            }

            process[numProcesses++] = info; 
        }
    }

    break;
}

case WM_PROCESS_ENDED:
{
    HANDLE hProcess = (HANDLE)lParam;

    for (int i = 0; i < numProcesses; ++i)
    {
        if (process[i].hProcess == hProcess)
        {
            UnregisterWait(process[i].hWait);

            for (int j = i + 1; j < numProcesses; ++j)
                process[j-1] = process[j];

            --numProcesses;

            break;
        }
    }

    CloseHandle(hProcess);
    ReleaseSemaphore(semafor, 1, NULL);
}

在这种情况下,你可以完全摆脱信号量,因为你已经有了自己的计数器:

const UINT WM_PROCESS_ENDED = WM_APP + 1;
const int MAX_PROCESSES = 10;

typedef struct procInfo
{
    HANDLE hProcess;
    HANDLE hWait;
} procInfo;

procInfo process[MAX_PROCESSES] = {};
int numProcesses = 0;
HWND hwndDialog;

...

VOID CALLBACK ProcessEnded(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
    PostMessage(hwndDialog, WM_PROCESS_ENDED, 0, (LPARAM)lpParameter);
}

...

case WM_INITDIALOG:
{
    hwndDialog = hwnd;
    break;
}

case WM_DESTROY:
{
    for (int i = 0; i < numProcesses; ++i)
    {
        UnregisterWaitEx(wait[i], INVALID_HANDLE_VALUE);
        TerminateProcess(process[i], 2);
        CloseHandle(process[i]);
    }

    hwndDialog = NULL;

    break;
}

case WM_COMMAND:
    switch (LOWORD(wParam))
    {
        case ID_OK:
        {
            if (numProcesses >= MAX_PROCESSES)
            {
                // too many instances running...
                break;
            }

            if (!CreateProcess("C:\\Windows\\System32\\notepad.exe",
                NULL, NULL, NULL, TRUE, 0, NULL, NULL,
                &si, &pi))
            {
                printf("Eroare la crearea procesului fiu!\n");
                break;
            }

            CloseHandle(pi.hThread);

            dwWaitForChild = WaitForInputIdle(pi.hProcess, 2000);
            switch (dwWaitForChild)
            {
                case 0:
                    printf("Procesul fiu este ready!\n");
                    break;

                case WAIT_TIMEOUT:
                    printf("Au trecut 2 sec. si procesul fiu nu este ready!\n");
                    break;

                case WAIT_FAILED:
                    printf("Eroare!\n");
                    break;
            }

            procInfo info;

            info.hProcess = pi.hProcess;
            if (!RegisterWaitForSingleObject(&info.hWait, pi.hProcess, &ProcessEnded, pi.hProcess, INFINITE, WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE))
            {
                TerminateProcess(pi.hProcess, 2);
                break;
            }

            process[numProcesses++] = info; 
        }
    }

    break;
}

case WM_PROCESS_ENDED:
{
    HANDLE hProcess = (HANDLE)lParam;

    for (int i = 0; i < numProcesses; ++i)
    {
        if (process[i].hProcess == hProcess)
        {
            UnregisterWait(process[i].hWait);

            for (int j = i + 1; j < numProcesses; ++j)
                process[j-1] = process[j];

            --numProcesses;

            break;
        }
    }

    CloseHandle(hProcess);
}