在 C++ 中,另外两个不同的共享对象可以从第三个共享对象访问 Singleton 吗?
In C++, can two other different shared objects access a Singleton from a third shared object?
我有一个 C++ 应用程序,它通过共享对象从两个或多个插件(每个插件至少有一个线程)加载大部分代码。我使用以下代码加载插件:
pluginHandle = dlopen(fileName, RTLD_NOW|RTLD_GLOBAL);
init_t* init = (init_t*) dlsym(pluginHandle, "init") // Create should return an instance of the class of the plugin
plugin = init();
我到了需要两个插件才能开始将数据添加到公共 Queue
的地步。由于应用程序不允许在不更改应用程序本身的代码的情况下在两个插件之间进行通信(这是我们试图避免的一点),我想我找到了解决这个问题的方法:第三个插件,其中包括一个单例 class 线程安全 Queue
.
然后我会重新编译和 link 两个针对库的插件,并使用 getInstance()
获取单例并开始将任务添加到队列中。
这是安全的实施方式吗?单例 Queue
可以吗?
A dynamic library (shared object), which includes a singleton class with a thread-safe Queue.
当你想限制一个class只被实例化一次时使用单例。那不是您想要的:您希望所有插件都在 class 的特定实例上工作。这里没有"only one can live"要求
C++11 中使用 Meyer 模式的线程安全单例可能如下所示:
class Singleton
{
private:
Singleton();
public:
Singleton(const &Singleton) = delete;
Singleton& operator=(const &Singleton) = delete;
static Singleton& get_instance()
{
static Singleton s;
return s;
}
};
默认构造函数被声明为私有,copy/assignment 操作被删除以避免多个实例。
您需要更简单的东西:一个始终返回相同实例的函数。像这样:
class Manager
{
public:
static Resource& get_resource()
{
static Resource r;
return r;
}
};
不需要防止多重实例化:如果你想要相同的实例,只需要相同的实例。
您还可以使用资源池扩展设计,在给定一些 id 的情况下返回相同的实例:
enum class ResourceId
{
ID_FOR_A_FAMILY_OF_PLUGIN,
ID_FOR_AN_OTHER_FAMILY_OF_PLUGIN
};
class Pool
{
public:
static Resource& get_resource(ResourceId id)
{
static std::map<ResourceId, Resource> p;
return p[id];
}
};
请注意,在此示例中,p[id]
是使用 Resource
的默认构造函数动态创建的。您可能希望在构造过程中传递参数:
class Resource
{
public:
Resource():ready(false){}
void init(some parameters)
{
// do some intialization
ready = true;
}
bool is_ready() const { return ready; }
private:
bool ready;
};
class Pool
{
public:
static Resource& get_resource(ResourceId id)
{
static std::map<ResourceId, Resource> p;
auto& r = p[id];
if(!r.is_ready())
{
r.init(some parameters);
}
return r;
}
};
或者,使用指针允许多态:
class Pool
{
public:
static std::unique_ptr<Resource>& get_resource(ResourceId id)
{
static std::map<ResourceId, std::unique_ptr<Resource>> p;
auto& r = p[id];
if(!r)
{
r = std::make_unique<SomeResourceTypeForId>(some parameters);
}
return r;
}
};
请注意,最后两个实现需要非静态代码周围的互斥体才能保证线程安全。
我有一个 C++ 应用程序,它通过共享对象从两个或多个插件(每个插件至少有一个线程)加载大部分代码。我使用以下代码加载插件:
pluginHandle = dlopen(fileName, RTLD_NOW|RTLD_GLOBAL);
init_t* init = (init_t*) dlsym(pluginHandle, "init") // Create should return an instance of the class of the plugin
plugin = init();
我到了需要两个插件才能开始将数据添加到公共 Queue
的地步。由于应用程序不允许在不更改应用程序本身的代码的情况下在两个插件之间进行通信(这是我们试图避免的一点),我想我找到了解决这个问题的方法:第三个插件,其中包括一个单例 class 线程安全 Queue
.
然后我会重新编译和 link 两个针对库的插件,并使用 getInstance()
获取单例并开始将任务添加到队列中。
这是安全的实施方式吗?单例 Queue
可以吗?
A dynamic library (shared object), which includes a singleton class with a thread-safe Queue.
当你想限制一个class只被实例化一次时使用单例。那不是您想要的:您希望所有插件都在 class 的特定实例上工作。这里没有"only one can live"要求
C++11 中使用 Meyer 模式的线程安全单例可能如下所示:
class Singleton
{
private:
Singleton();
public:
Singleton(const &Singleton) = delete;
Singleton& operator=(const &Singleton) = delete;
static Singleton& get_instance()
{
static Singleton s;
return s;
}
};
默认构造函数被声明为私有,copy/assignment 操作被删除以避免多个实例。
您需要更简单的东西:一个始终返回相同实例的函数。像这样:
class Manager
{
public:
static Resource& get_resource()
{
static Resource r;
return r;
}
};
不需要防止多重实例化:如果你想要相同的实例,只需要相同的实例。
您还可以使用资源池扩展设计,在给定一些 id 的情况下返回相同的实例:
enum class ResourceId
{
ID_FOR_A_FAMILY_OF_PLUGIN,
ID_FOR_AN_OTHER_FAMILY_OF_PLUGIN
};
class Pool
{
public:
static Resource& get_resource(ResourceId id)
{
static std::map<ResourceId, Resource> p;
return p[id];
}
};
请注意,在此示例中,p[id]
是使用 Resource
的默认构造函数动态创建的。您可能希望在构造过程中传递参数:
class Resource
{
public:
Resource():ready(false){}
void init(some parameters)
{
// do some intialization
ready = true;
}
bool is_ready() const { return ready; }
private:
bool ready;
};
class Pool
{
public:
static Resource& get_resource(ResourceId id)
{
static std::map<ResourceId, Resource> p;
auto& r = p[id];
if(!r.is_ready())
{
r.init(some parameters);
}
return r;
}
};
或者,使用指针允许多态:
class Pool
{
public:
static std::unique_ptr<Resource>& get_resource(ResourceId id)
{
static std::map<ResourceId, std::unique_ptr<Resource>> p;
auto& r = p[id];
if(!r)
{
r = std::make_unique<SomeResourceTypeForId>(some parameters);
}
return r;
}
};
请注意,最后两个实现需要非静态代码周围的互斥体才能保证线程安全。