2013-11-20 104 views
1

我有以下代碼:正確刪除單

MyClass.h:

static MyMutex instanceMutex; 
static MyClass* getInstance(); 
static void deleteInstance(); 

MyClass.c:

MyMutex MyClass::instanceMutex; 

MyClass* MyClass::getInstance() 
{ 
    if (theInstance == 0) 
    { 
     instanceMutex.acquire(); 
     if (theInstance == 0) 
     { 
      theInstance = new MyClass(); 
     } 
     instanceMutex.release(); 
    } 
    return theInstance; 
} 

void MyClass::deleteInstance() 
{ 
    if (theInstance != 0) 
    { 
     instanceMutex.acquire(); 
     if (theInstance != 0) 
     { 
      theInstance->finalize(); 
      delete theInstance; 
      theInstance = 0; 
     } 
     instanceMutex.release(); 
    } 
    return; 
} 

我對此有2個問題:

  • 上述代碼是否正確且安全?
  • 後我稱之爲 '刪除theInstance' MyClass中:: deleteInstance(),我然後調用

    • theInstance = 0;
    • instanceMutex.release();

    但是,如果實例被刪除,那怎麼可能呢?這不是班級的記憶嗎?

+2

雙檢鎖和一個單身一下子!天啊。 –

+3

你打算在多線程環境中使用這個單例嗎?如果不是,爲什麼鎖定?如果是,爲什麼刪除單身人士的方法?順便說一句,爲什麼在程序執行期間需要刪除單身人士? – ArturFH

+0

finalize()取消線程 – Kam

回答

13

如果它是一個單 - 它被定義爲有一個確切的實例 - 如果你刪除它 - 這個下降到0

因此,看來你不應該支持所有

-3

getInstance和delteInstance應該是靜態的,所以它們只能用於該類的靜態成員。靜態成員不會被實例破壞。

如果您的代碼是多線程的,則代碼不安全。沒有辦法確保沒有指向在某個運行環境中保存的實例的指針。

+1

你怎麼知道它們已經不是靜態的? –

+1

他們是靜態的:) – Kam

+0

他們應該是靜態的。這就是模式的實施方式。 – Sorin

0

這裏刪除是一個問題:

if (theInstance == 0) // <- Some other thread might see a non-null theInstance 
{      // before the constructor below returns 

    instanceMutex.acquire(); 
    if (theInstance == 0) 
    { 
     theInstance = new MyClass(); // <- This might set theInstance to something 
             // before the constructor returns. 
    } 
    instanceMutex.release(); 
} 

您可能要實現某種形式的引用計數系統(如使用shared_ptr),並以類似的方式進行初始化,雖然照顧,以確保在初始化完成之前它的實例指針未設置。

如果你喜歡冒險,你也可以嘗試:

if (theInstance == 0) 
{ 
    instanceMutex.acquire(); 
    if (theInstance == 0) 
    { 
     MyClass * volatile ptr = new MyClass(); 
     __FuglyCompilerSpecificFenceHintThatMightNotActuallyDoAnything(); 
     theInstance = ptr; // <- Much hilarity may ensue if this write is not atomic. 
    } 
    instanceMutex.release(); 
} 

這可能會解決它,它可能不會。在第二種情況下,它取決於編譯器如何處理volatile,並且天氣或不是指針大小的寫入是原子的。

+0

爲什麼不使用C++ 11的['std :: atomic_thread_fence'](http://en.cppreference.com/w/cpp/atomic/atomic_thread_fence)? – Void

0

我傾向於採用用C++單身以下列方式:

class Singleton 
{ 
public: 
    static Singleton& instance() 
    { 
     static Singleton theInstance; 
     return theInstance; 
    } 

    ~Singleton() 
    { 
     // Free resources that live outside the processes life cycle here, 
     // if these won't automatically be released when the occupying process 
     // dies (is killed)! 
     // Examples you don't have to care of usually are: 
     // - freeing virtual memory 
     // - freeing file descriptors (of any kind, plain fd, socket fd, whatever) 
     // - destroying child processes or threads 
    } 

private: 
    Singleton() 
    { 
    } 

    // Forbid copies and assignment 
    Singleton(const Singleton&); 
    Singleton& operator=(const Singleton&); 
}; 

您可以也鎖定機制,以防止多個線程的併發實例化(將當然需要一個靜態互斥!)。但刪除留給這裏(操作系統特定)的過程上下文。
通常,您不需要關心刪除單例類以及如何釋放其獲取的資源,因爲這些操作都是在進程死亡時由OS處理的。
無論如何,可能有用例,當你想讓你的單身人士課程在程序崩潰的情況下的一些備份點。大多數C++ crt實現都支持調用靜態分配實例的析構函數,但是您應該注意不要對任何這些析構函數進行有序的依賴關係。

0

在一個項目中,我們繼承了一些外部公司,我看到了一個由有人刪除單身人士造成的整個噩夢類錯誤。也許在罕見的情況下,刪除單例對整個應用程序沒有任何副作用,因爲您可以確定這個單例沒有使用實例,但通常這是一個糟糕設計的好例子。有人可以從你的例子中學習,這將是一個糟糕的,甚至是有害的課程。如果你需要清理的東西在單身時,應用程序退出,使用模式第i個返回參考單身,就像例如:

#include <iostream> 

using namespace std; 

class singleton { 
    private: 
     singleton() { 
      cout << "construktor\n"; 
     } 
     ~singleton() { 
      cout << "destructor\n"; 
     } 
    public: 
     static singleton &getInstance() { 
      static singleton instance; 
      cout << "instance\n"; 
      return instance; 
     } 
     void fun() { 
      cout << "fun\n"; 
     } 
}; 


int main() { 
    cout << "one\n"; 
    singleton &s = singleton::getInstance(); 
    cout << "two\n"; 
    s.fun(); 
    return 0; 
} 
+0

沒有這種變體不保證在沒有同步機制的情況下是線程安全的。除了實例,您還需要一個靜態互斥鎖才能使線程安全。 –

+0

@ g-makulik erm,你是對的。我將這些信息從答案中刪除,不要混淆潛在的讀者。 – ArturFH