最近在写代码时发现了一个很奇怪的现象,单例模式生成的单例实例在同一进程中出现了多次。在此记录一下。
下面是我复现的代码,也可以直接在我的仓库进行下载
gitee
git clone https://gitee.com/little-sweet-cookie/singleton_so_test.git |
代码包括五个代码文件和一个 makefile 文件
handler.h
main.cc
service_a.cc
service_b.cc
singleton.h
makefile
需要的单例实例,使用单例模板实现单例模式
// handler.h | |
#ifndef HANDLER_H | |
#define HANDLER_H | |
#include "singleton.h" | |
#include <iostream> | |
class Handler { | |
public: | |
void show() | |
{ | |
std::cout << "Handler::instance " << (long long)this << std::endl; | |
} | |
}; | |
#endif // !HANDLER_H |
单例模板
// singleton.h | |
#ifndef SINGLETON_H | |
#define SINGLETON_H | |
#include <memory> | |
template<typename T> | |
class Singleton | |
{ | |
public: | |
static std::shared_ptr<T> GetInstance() | |
{ | |
if (instance == nullptr) { | |
instance = std::make_shared<T>(); | |
} | |
return instance; | |
} | |
Singleton(T&&) = delete; | |
Singleton(const T&) = delete; | |
void operator= (const T&) = delete; | |
protected: | |
Singleton() = default; | |
virtual ~Singleton() = default; | |
static std::shared_ptr<T> instance; | |
}; | |
template<typename T> | |
std::shared_ptr<T> Singleton<T>::instance; | |
#endif // !SINGLETON_H |
业务 A
// service_a.cc | |
#include <iostream> | |
#include "handler.h" | |
#include "singleton.h" | |
extern "C" { | |
void show_service_a() | |
{ | |
Singleton<Handler>::GetInstance()->show(); | |
std::cout << "aaa" << std::endl; | |
} | |
} |
业务 B
// service_b.cc | |
#include <iostream> | |
#include "handler.h" | |
#include "singleton.h" | |
extern "C" { | |
void show_service_b() | |
{ | |
Singleton<Handler>::GetInstance()->show(); | |
std::cout << "bbb" << std::endl; | |
} | |
} |
主函数
// main.cc | |
#include <dlfcn.h> | |
#include <iostream> | |
#include "handler.h" | |
void show_main() | |
{ | |
Singleton<Handler>::GetInstance()->show(); | |
std::cout << "main" << std::endl; | |
} | |
int main() | |
{ | |
show_main(); | |
void * handle; | |
using func = void (*)(); | |
handle = dlopen("lib_service_a.so", RTLD_LAZY); | |
func show_service_a = (func)dlsym(handle, "show_service_a"); | |
show_service_a(); | |
handle = dlopen("lib_service_b.so", RTLD_LAZY); | |
func show_service_b = (func)dlsym(handle, "show_service_b"); | |
show_service_b(); | |
return 0; | |
} |
Makefile 文件
// makefile | |
service_a_so:service_a.cc | |
g++ -fPIC -shared service_a.cc -o lib_service_a.so | |
service_b_so:service_b.cc | |
g++ -fPIC -shared service_b.cc -o lib_service_b.so | |
main_so:main.cc | |
g++ -fPIC -shared main.cc -o lib_main.so | |
clean: | |
rm -f main.out lib_main.so | |
rm -f lib_service_a.so | |
rm -f lib_service_b.so | |
all:clean service_a_so service_b_so main.cc | |
g++ main.cc -o main.out -Wl,-rpath=./ | |
./main.out | |
all_so:clean service_a_so service_b_so main_so | |
g++ lib_main.so -o main.out -Wl,-rpath=./ | |
./main.out |
诡异现象
- 当我使用
make all
时,结果如下cookie@cookie-VirtualBox:~/repo/test_singleton$ make all
rm -f main.out lib_main.so
rm -f lib_service_a.so
rm -f lib_service_b.so
g++ -fPIC -shared service_a.cc -o lib_service_a.so
g++ -fPIC -shared service_b.cc -o lib_service_b.so
g++ main.cc -o main.out -Wl,-rpath=./
./main.out
Handler::instance 102966796923584
main
Handler::instance 102966796926512
aaa
Handler::instance 102966796926512
bbb
cookie@cookie-VirtualBox:~/repo/test_singleton$
- 当我使用
make all_so
时,结果如下rm -f main.out lib_main.so
rm -f lib_service_a.so
rm -f lib_service_b.so
g++ -fPIC -shared service_a.cc -o lib_service_a.so
g++ -fPIC -shared service_b.cc -o lib_service_b.so
g++ -fPIC -shared main.cc -o lib_main.so
g++ lib_main.so -o main.out -Wl,-rpath=./
./main.out
Handler::instance 94236187448000
main
Handler::instance 94236187448000
aaa
Handler::instance 94236187448000
bbb
cookie@cookie-VirtualBox:~/repo/test_singleton$
可以看到, make all
时,会生成两个不同的单例,业务 A 与 业务 B 为同一个实例,main 为另一个实例。
而使用 make all_so
时,所有 so 中的单例都是同一个实例。