【问题标题】:Nested locks (critical section) not working嵌套锁(关键部分)不起作用
【发布时间】:2013-05-01 14:50:46
【问题描述】:

我正在学习关键部分(用于多线程),我在网上找到了一个使用它的课程。我不明白为什么我的代码不起作用 - 我应该在控制台显示上获得“成功”,但我没有。

我是否错误地锁定了它?我确信我可以准确地进入和退出这些部分 - 但我不知道为什么第三个线程 (mul) 似乎不起作用。

这是主要代码(在 VS 2012 上执行此操作):

#include "stdafx.h"
#include <windows.h>
#include <process.h>
#include <iostream>
#include <assert.h>
#include <queue>
#include "Lock.h"

//File: CriticalSectionExample.cpp

#define MAX_THREADS 2

using namespace std;

static unsigned int counter = 100;
static bool alive = true;
static examples::Lock lock_1;
static examples::Lock lock_2;

queue<int> test_q;
queue<int> later_q;

static unsigned __stdcall sub(void *args)
{
    while(alive)
    {
        cout << "tq";
        lock_1.acquire();
        test_q.push(1);
        lock_1.release();

        ::Sleep(500);
    }
    return 0;
}

static unsigned __stdcall add(void *args)
{
    while(alive)
    {
        if (!test_q.empty())
        {
            int first = test_q.front();
            //cout << first << endl;

            lock_1.acquire();
            test_q.pop();
            lock_1.release();

            lock_2.acquire();
            cout << "lq" << first << endl;
            later_q.push(first);
            lock_2.release();
        }

        ::Sleep(500);
    }
    return 0;
}

static unsigned __stdcall mul(void *args)
{
    while(alive)
    {
        if (!later_q.empty())
        {
            cout << "success" << endl;
            lock_2.acquire();
            test_q.pop();
            lock_2.release();
        }

        ::Sleep(500);
    }
    return 0;
}

int main()
{
    // create threads
    unsigned tadd;
    HANDLE hadd = (HANDLE) ::_beginthreadex(0, 0, &add, 0, CREATE_SUSPENDED, &tadd);
    assert(hadd != 0);

    unsigned tsub;
    HANDLE hsub = (HANDLE) ::_beginthreadex(0, 0, &sub, 0, CREATE_SUSPENDED, &tsub);
    assert(hsub != 0);

     unsigned tmul;
    HANDLE hmul = (HANDLE) ::_beginthreadex(0, 0, &mul, 0, CREATE_SUSPENDED, &tsub);
    assert(hmul != 0);

    // start threads
    ::ResumeThread(hadd);
    ::ResumeThread(hsub);

    ::Sleep(10000); // let threads run for 10 seconds

    // stop & cleanup threads
    alive = false;
    ::WaitForSingleObject(hsub, INFINITE);
    ::CloseHandle(hsub);
    ::WaitForSingleObject(hadd, INFINITE);
    ::CloseHandle(hadd);

    return 0;
}

这是包含关键部分的头文件:

#ifndef _Lock_H_
#define _Lock_H_


#include <windows.h>

/**
*@description: A simple Lock implementation using windows critical section object
*/

namespace examples
{
    class Lock
    {
    public:
        Lock()
        {
            ::InitializeCriticalSection(&m_cs);
        }

        ~Lock()
        {
            ::DeleteCriticalSection(&m_cs);
        }

        void acquire()
        {
            ::EnterCriticalSection(&m_cs);
        }

        void release()
        {
            ::LeaveCriticalSection(&m_cs);
        }

    private:
        Lock(const Lock&);
        Lock& operator=(const Lock&);

        CRITICAL_SECTION m_cs;
    };

}

#endif //_Lock_H_

【问题讨论】:

    标签: c++ multithreading critical-section


    【解决方案1】:

    您似乎忘记恢复您的第三个线程。

    我想说您的代码中有几个潜在的缺陷。我建议您锁定使用共享变量的每个位置(是的,即使您只读取它们的值)。这次它可能会起作用,但有时即使读取正在被其他线程修改的对象也可能很危险。

    您还可以将更复杂的 Free/Lock 模式应用于您的代码,因此您无需手动调用获取/释放

    class AutoLock
    {
    public:
        AutoLock(Lock& l)
        :lock(l)
        {
            lock.acquire();
        }
    
        ~AutoLock()
        {
            lock.release();
        }
    
        Lock& lock;
     };
    

    然后你可以重写你的函数,让它们更干净、更安全:

    static unsigned __stdcall sub(void *args)
    {
        while(alive)
        {
            cout << "tq";
            {
                examples::AutoLock lock(lock_1);
                test_q.push(1);
            }
            ::Sleep(500);
        }
        return 0;
    }
    

    请注意,需要单独的范围,否则关键部分 lock_1 将被锁定,直到 ::Sleep(500) 执行,这不是您通常想要的。

    【讨论】:

    • 谢谢你。但是在 VS2012 上,有一个与 AutoLock(Lock&amp; l) 相关的错误 - 它一直告诉我它需要一个 )。那有什么问题?它适用于 VS2010,但不适用于 2012。
    • 也许你试图在 Lock 声明之上声明 AutoLock 类。在这种情况下,编译器不知道 Lock 是什么,因此期待别的东西。如果这不是这里的问题复制错误,则可能与首先出现此错误的编译日志有关。
    • 我做了一件愚蠢的事(把'l'看作'1'),这就是问题所在。非常感谢您!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-13
    • 1970-01-01
    • 2013-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    相关资源
    最近更新 更多