Как сделать так, чтобы два потока строго чередовались с использованием мьютексов? - PullRequest
3 голосов
/ 30 октября 2011

Мне нужно создать две темы, которые строго чередуются.Вот пример кода, который я использую:

#include <Windows.h>
#include <iostream>
using std::cout;
using std::endl;

HANDLE g_hMutex1;
HANDLE g_hMutex2;

DWORD WINAPI ThreadFunc1(LPVOID lpParam);
DWORD WINAPI ThreadFunc2(LPVOID lpParam);

int main(void)
{
    int nCalcNumber = 10;
    DWORD dwThreadId;
    HANDLE pThreadHandles[2];

    g_hMutex1 = CreateMutex(NULL, FALSE, NULL);
    g_hMutex1 = CreateMutex(NULL, FALSE, NULL);

    pThreadHandles[0] = CreateThread(
        NULL,
        0,
        ThreadFunc1,
        static_cast<void*>(&nCalcNumber),
        0,
        &dwThreadId);

    pThreadHandles[1] = CreateThread(
        NULL,
        0,
        ThreadFunc2,
        static_cast<void*>(&nCalcNumber),
        0,
        &dwThreadId);

    WaitForMultipleObjects(2, pThreadHandles, TRUE, INFINITE);

    CloseHandle(pThreadHandles[0]);
    CloseHandle(pThreadHandles[1]);
    CloseHandle(g_hMutex1);
    CloseHandle(g_hMutex2);

    return 0;
}

DWORD WINAPI ThreadFunc1(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*>(lpParam);

    for (int i = 0; i < *nCalcNumber; i++)
    {
        WaitForSingleObject(g_hMutex1, INFINITE);

        cout << "Func 1" << endl;

        ReleaseMutex(g_hMutex1);
    }

    return 0;
}

DWORD WINAPI ThreadFunc2(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*>(lpParam);

    for (int i = 0; i < *nCalcNumber; i++)
    {
        WaitForSingleObject(g_hMutex1, INFINITE);

        cout << "Func 2" << endl;

        ReleaseMutex(g_hMutex1);
    }

    return 0;
}

И результат, который я ожидаю получить:

 Func 1
 Func 2
 Func 1
 Func 2
 Func 1
 Func 2
 ...and so one

Что нужно добавить, чтобы получить желаемый результат.Могу ли я использовать для этого второй мьютекс?

Ответы [ 3 ]

7 голосов
/ 30 октября 2011

Как отмечалось в других ответах, семафор - намного лучший выбор, чем мьютекс.Но как чисто академическое упражнение (домашнее задание?), Вы можете сделать это и с мьютексом.(Акцент: это чисто академическое упражнение. Настоящая программа не должна использовать эту технику.)

DWORD WINAPI ThreadFunc1(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*>(lpParam);

    WaitForSingleObject(g_hMutex2, INFINITE);
    for (int i = 0; i < *nCalcNumber; i++)
    {
        WaitForSingleObject(g_hMutex1, INFINITE);
        ReleaseMutex(g_hMutex2);

        cout << "Func 1" << endl;

        ReleaseMutex(g_hMutex1);
        WaitForSingleObject(g_hMutex2, INFINITE);
    }

    return 0;
}

DWORD WINAPI ThreadFunc2(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*>(lpParam);

    WaitForSingleObject(g_hMutex2, INFINITE);
    for (int i = 0; i < *nCalcNumber; i++)
    {
        WaitForSingleObject(g_hMutex1, INFINITE);
        ReleaseMutex(g_hMutex2);

        cout << "Func 2" << endl;

        ReleaseMutex(g_hMutex1);
        WaitForSingleObject(g_hMutex2, INFINITE);
    }

    return 0;
}

Мьютекс 1 - это мьютекс «У меня есть», а Мьютекс 2 - это «Я хочу это»следующий "мьютекс.

3 голосов
/ 30 октября 2011

Если вы можете использовать semaphore: вы можете использовать semaphore вместо mutex, его легко использовать так же, как mutex.

Этот код работает нормально:

#include <windows.h>
#include <iostream>
using std::cout;
using std::endl;

PHANDLE sem1;
PHANDLE sem2;

DWORD WINAPI ThreadFunc1(LPVOID lpParam);
DWORD WINAPI ThreadFunc2(LPVOID lpParam);

int main(void)
{
    int nCalcNumber = 10;
    DWORD dwThreadId;
    HANDLE pThreadHandles[2];

    sem1 = (PHANDLE) CreateSemaphore(NULL, 1, 1, NULL);
    sem2 = (PHANDLE) CreateSemaphore(NULL, 0, 1, NULL);


    pThreadHandles[0] = CreateThread(
                                     NULL,
                                     0,
                                     ThreadFunc1,
                                     static_cast<void*> (&nCalcNumber),
                                     0,
                                     &dwThreadId);

    pThreadHandles[1] = CreateThread(
                                     NULL,
                                     0,
                                     ThreadFunc2,
                                     static_cast<void*> (&nCalcNumber),
                                     0,
                                     &dwThreadId);

    WaitForMultipleObjects(2, pThreadHandles, TRUE, INFINITE);

    CloseHandle(pThreadHandles[0]);
    CloseHandle(pThreadHandles[1]);
    CloseHandle(sem1);
    CloseHandle(sem2);

    return 0;
}

DWORD WINAPI ThreadFunc1(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*> (lpParam);

    for (int i = 0; i < *nCalcNumber; i++)
    {
        WaitForSingleObject(sem1, INFINITE);

        cout << "Func 1" << endl;

        ReleaseSemaphore(sem2, 1 ,NULL);
    }

    return 0;
}

DWORD WINAPI ThreadFunc2(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*> (lpParam);

    for (int i = 0; i < *nCalcNumber; i++)
    {

        WaitForSingleObject(sem2, INFINITE);

        cout << "Func 2" << endl;

        ReleaseSemaphore(sem1, 1 ,NULL);
    }

    return 0;
}
2 голосов
/ 30 октября 2011

Вы предполагаете, что ОС действительно поддерживает это. Windows не делает. У него нет никаких гарантий в отношении графика, кроме голодания.

Итак, вам нужно установить переменную флага, чтобы каждый поток изменил ее, чтобы позволить другому потоку работать. Например, если это правда - запустить, если это ложно - освободить мьютекс и некоторое время спать, а другой поток - с точностью до наоборот. Здесь важен сон, чтобы избежать голода и тупика. Я думаю, что это может быть Sleep (0) (проверьте, означает ли это «yield» в Windows, я не уверен).

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

Его можно легко изменить на более чем 2 потока, сделав переменную счетчиком по модулю количества потоков, и каждый поток увеличит значение в конце цикла и проверив значение по модулю как номер потока в порядке исполнения в начале.

1010 * редактировать *

volatile bool flag = false;

DWORD WINAPI ThreadFunc1(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*>(lpParam);

    for (int i = 0; i < *nCalcNumber; /*no-op*/;)
    {
        WaitForSingleObject(g_hMutex1, INFINITE);

        if (flag) {Sleep(0); continue;}

        cout << "Func 1" << endl;

        flag = true;
        i++;
        ReleaseMutex(g_hMutex1);
    }

    return 0;
}

DWORD WINAPI ThreadFunc2(LPVOID lpParam)
{
    int* nCalcNumber = static_cast<int*>(lpParam);

    for (int i = 0; i < *nCalcNumber; /*no-op*/;)
    {
        WaitForSingleObject(g_hMutex1, INFINITE);
        if (!flag) {Sleep(0); continue;}

        cout << "Func 2" << endl;

        flag = false;
        i++;
        ReleaseMutex(g_hMutex1);
    }

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...