带有信号的ThreadSafe Singleton的问题

issue with Threadsafe singleton with semaphore

本文关键字:Singleton 问题 ThreadSafe 信号      更新时间:2023-10-16

我写了一个简单的单例应用程序。

以下是我的样本主要类

// ThreadsafeSingletonUsingSemaphore.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include "MySingleton.h"
using namespace std;
int i =0;
#define THREADCOUNT 100
DWORD WINAPI ThreadProc(LPVOID lParam);
HANDLE g_semaphore = NULL;
int _tmain(int argc, _TCHAR* argv[])
{
    g_semaphore = CreateSemaphore(NULL,1,1,_T("TreadOne"));
    HANDLE hThread[THREADCOUNT];
    DWORD aThreadID;
    for(int iCount = 0; iCount < THREADCOUNT ; iCount++)
    {
        hThread[iCount] = CreateThread(NULL, 0, ThreadProc, 0,0, &aThreadID);
        if( hThread[iCount] == NULL )
        {
            cout<<"CreateThread error: %d" << GetLastError() << endl;
            return 1;
        }
    }
    WaitForMultipleObjects(THREADCOUNT, hThread, TRUE, INFINITE);
    // Close thread and semaphore handles
    for(int i=0; i < THREADCOUNT; i++ )
        CloseHandle(hThread[i]);
    cout << MySingleton::getInstance().getCounter() << endl ;
    CloseHandle(g_semaphore);
    _getch();
    return 0;
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
    //DWORD result = WaitForSingleObject(g_semaphore,INFINITE);
    //if(WAIT_OBJECT_0 == result)
        MySingleton::getInstance().incrementCouner();
    //ReleaseSemaphore(g_semaphore,1, NULL);
    return TRUE;
}

这是我的Singleton实施类。

#include "StdAfx.h"
#include "MySingleton.h"
MySingleton* MySingleton::m_instance = NULL;
HANDLE MySingleton::m_hSem = CreateSemaphore(NULL, 1, 1, _T("MySingleton"));
HANDLE MySingleton::m_One = CreateSemaphore(NULL, 1, 1, _T("MyOne"));
MySingleton::MySingleton(void) : m_counter(0)
{
}
MySingleton::~MySingleton(void)
{
    cout << "destructor" << endl;
    CloseHandle(m_hSem);
    CloseHandle(m_One);
}
MySingleton& MySingleton::getInstance()
{
    DWORD result = WaitForSingleObject(m_hSem, INFINITE);
    if(WAIT_OBJECT_0 == result)
    {
        if(m_instance == NULL)
        {
            cout << "creating" << endl;
            m_instance = new MySingleton();
        }
    }
    ReleaseSemaphore(m_hSem,1,NULL);
    return *m_instance;
}
void MySingleton::setCouner(int iCount_in)
{
    m_counter = iCount_in;
}
int MySingleton::getCounter()
{
    return m_counter;
}
void MySingleton::incrementCouner() 
{ 
    DWORD result = WaitForSingleObject(m_One, INFINITE);
    if(WAIT_OBJECT_0 == result)
        m_counter++;
    ReleaseSemaphore(m_One,1,NULL);
}

这是我的.H班。

#pragma once
#include <windows.h>
#include <iostream>
#include <conio.h>
using namespace std;
class MySingleton
{
private:
    static HANDLE m_hSem, m_One;
    HANDLE m_hCountSem;
    static MySingleton* m_instance;
    int m_counter;
    MySingleton();
    MySingleton(const MySingleton& obj_in);
    MySingleton& operator=(const MySingleton& obj_in);
public:
    ~MySingleton(void);
    static MySingleton& getInstance();
    void setCouner(int iCount_in);
    int getCounter();
    void incrementCouner();
};

问题是计数器的最终价值从来没有100。当我在创建每个线程之前在主机中引入睡眠时,它可以正常工作。

问题是呼叫WaitForMultipleObjectsMAXIMUM_WAIT_OBJECTS处理至少在Visual Studio 2017中为64。

请注意如何致电WaitForMultipleObjects加入线程返回WAIT_FAILED

为了等待更多对象,应该根据文档:

等待超过最大_wait_objects手柄,使用以下方法之一:

  • 创建一个线程以等待最大_wait_objects手柄,然后在该线程以及另一个手柄上等待。使用此技术将手柄分解为最大_wait_objects。
  • 呼叫registerwaitforsingleobject在每个句柄上等待。线程池中的等待线程在最大_wait_objects注册的对象上等待,并在对象发出信号或超时间隔后分配工作线程。

您不需要编写所有代码。实现线程安全单位的最简单方法是使用Scott Meyer的Singleton Idiom:

class Singleton {
    int counter;
    mutable std::mutex counter_guard;
    Singleton() {}
public:
    Singleton(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton& operator=(Singleton&&) = delete;
    static Singleton& instance() {
        static Singleton theInstance;
        return theInstance;
    }
    void setCounter(int newVal) {
        std::unique_lock<std::mutex> lock(counter_guard);
        counter = newVal;
    }  
    void incrementCounter() {
        std::unique_lock<std::mutex> lock(counter_guard);
        ++counter;
    }  
    int getCounter() const {
        std::unique_lock<std::mutex> lock(counter_guard);
        return counter;
    }  
};

一种更简单的方法是将std::atomic<int>类型用于counter成员变量。然后,完全可以省略Mutex和Lock Guards。