我创建了一个简单的共享库,其中包含单例类,并且我希望此类具有相应的行为,即对于使用它的所有应用程序来说,它将是一个真正的单例。但实际上,事实证明它的工作方式有所不同。每个使用我的共享库的应用程序都会创建自己的单例实例,这根本不符合我的计划。
这是共享库的代码:
singleton.h
#ifndef SINGLETON_H
#define SINGLETON_H
#if defined _WIN32 || defined __CYGWIN__
#ifdef BUILDING_DLL
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllexport))
#else
#define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
#endif
#else
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllimport))
#else
#define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
#endif
#endif
#define DLL_LOCAL
#else
#if __GNUC__ >= 4
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define DLL_PUBLIC
#define DLL_LOCAL
#endif
#endif
class DLL_PUBLIC Singleton
{
public:
static Singleton &instance();
int test();
private:
Singleton();
int m_num;
};
#endif
singleton.cpp
#include "singleton.h"
Singleton &Singleton::instance()
{
static Singleton singleton;
return singleton;
}
Singleton::Singleton() :
m_num(0)
{ }
int Singleton::test()
{
return ++m_num;
}
编译和链接如下:
g++ -c -pipe -fPIC -o singleton.o singleton.cpp
g++ -rdynamic -export-dynamic -shared -Wl,-soname,libSingletonLib.so.1 -o libSingletonLib.so.1.0.0 singleton.o
小型测试实用程序:
main.cpp
#include <stdio.h>
#include "singleton.h"
int main(int argc, char *argv[])
{
int num = Singleton::instance().test();
printf("num: %d", num);
getchar();
return 0;
}
以及编译和链接选项:
g++ -c -pipe -g -std=gnu++11 -Wall -W -fPIC -o main.o main.cpp
g++ -Wl -o ../SingletonTest main.o -L.. -lSingletonLib
现在,我运行测试应用程序的2个实例,因此我希望它们都将使用单例,并且数量将增加。但是意外的输出是:
第一审:
./SingletonTest
num: 1
第二实例:
./SingletonTest
num: 1
注意:当第二个应用程序启动时,第一个应用程序仍在运行。
据我了解,应用程序的每个实例都会创建单例的不同实例。
如何避免这种情况,以便所有与同一共享库链接的实例都将仅使用一个单例?
我使用:
Ubuntu 18.04
gcc(Ubuntu 7.3.0-27ubuntu1〜18.04)7.3.0
更新: 好的,看来我必须使用共享内存或某些进程间通信。我以前从未使用过它,因此我将重新表述这个问题:如何将一个且只有一个单例用于多个过程?如何将其放在共享内存中?
答案 0 :(得分:0)
好吧,经过长时间的搜索,我想我已经找到了解决方案(或者解决方法)。由于某些原因,我完全忽略了共享库共享代码但不共享数据。使用相同共享库的每个应用程序都会创建其单例的副本。因此,我了解我需要按照注释中的建议使用IPC(感谢@ Gojita和@Mayur)。
这就是我来到的代码:
#include <new>
#include <errno.h>
...
Singleton &Singleton::instance()
{
//static Singleton instance;
//return instance;
static Singleton* instance = getSharedMemory();
return *instance;
}
Singleton * Singleton::getSharedMemory()
{
Singleton * instance = nullptr;
bool already_exists = false;
int shm = shm_open("my_memory", O_CREAT | O_RDWR | O_EXCL, 0777);
if(shm == -1)
{
if(errno == EEXIST)
{
already_exists = true;
shm = shm_open("my_memory", O_CREAT | O_RDWR , 0777);
}
if(shm == -1)
{
perror("shm_open error");
return nullptr;
}
}
void *addr = mmap(nullptr, sizeof(Singleton) + 1, PROT_WRITE | PROT_READ, MAP_SHARED, shm, 0);
if(!already_exists)
{
if (ftruncate(shm, sizeof(Singleton) + 1) == -1)
{
perror("ftruncate error");
return nullptr;
}
instance = new(addr) Singleton;
}
else
instance = reinterpret_cast<Singleton *>(addr);
return instance;
}
注意:在下面的代码中,我创建共享内存区域(名为“ my_memory”)。如果已经分配了内存(标志O_EXCL可以工作),那么我就使用它,否则,我将设置其大小(使用ftruncate),并通过在内存中放置new来创建单例。另外,您可以退出该应用程序,然后再次输入该应用程序并按原样使用单例-该类仍保留在内存中。可以通过调用munmap
和shm_unlink
或重新启动操作系统来释放内存。