如何使用互斥锁严格交替使用两个线程?

时间:2011-10-30 08:34:55

标签: c++ multithreading winapi

我需要创建两个严格交替的线程。以下是我使用的示例代码:

#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 个答案:

答案 0 :(得分:7)

正如其他答案所述,信号量是一种比互斥锁更好的选择。但作为纯粹的学术练习(家庭作业?),你也可以使用互斥量。 (重点:这是一个纯粹的学术练习。一个真正的程序不应该使用这种技术。)

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是“我想要它下一个”互斥锁。

答案 1 :(得分:3)

如果您可以使用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 :(得分:2)

你假设操作系统实际上支持这个。 Windows没有。它无法保证调度,除此之外没有饥饿。

所以你需要做的是设置一个标志变量,这样每个线程都会改变它以允许另一个线程运行。例如,如果它是真的 - 运行,如果它是假的 - 释放互斥锁并睡眠一段时间,而另一个线程 - 完全相反。睡眠在这里很重要,以避免饥饿和僵局。我认为它可以是Sleep(0)(检查它是否意味着Windows中的“产量”,我不确定)。

当然,检查应该在执行互斥锁时完成,并且在运行结束时,每个线程都会将变量更改为相反的 - 允许另一个线程运行并阻塞自己直到另一个线程运行然后改回来。

通过使变量成为计数器模数的线程数,并且每个线程在运行结束时增加值,并将模数值检查为线程的数字顺序,可以很容易地将其更改为2个以上的线程开头的执行。

修改

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;
}