在C中创建单例的最佳方法是什么?并发解决方案会很好。
我知道C不是你用于单身人士的第一种语言。
答案 0 :(得分:29)
首先,C不适合OO编程。如果你这样做,你会一直在战斗。其次,单例只是具有一些封装的静态变量。所以你可以使用静态全局变量。然而,全局变量通常具有与其相关的太多弊病。否则您可以使用函数本地静态变量,如下所示:
int *SingletonInt() {
static int instance = 42;
return &instance;
}
或更聪明的宏:
#define SINGLETON(t, inst, init) t* Singleton_##t() { \
static t inst = init; \
return &inst; \
}
#include <stdio.h>
/* actual definition */
SINGLETON(float, finst, 4.2);
int main() {
printf("%f\n", *(Singleton_float()));
return 0;
}
最后,请记住,单身人士大多被滥用。很难让它们正确,特别是在多线程环境下......
答案 1 :(得分:18)
你不需要。 C已经有全局变量,所以你不需要解决它们来模拟它们。
答案 2 :(得分:14)
它与C ++版本相同。只需要一个返回实例指针的函数。它可以是函数内部的静态变量。根据平台,使用临界区或pthread互斥体包裹功能体。
#include <stdlib.h>
struct A
{
int a;
int b;
};
struct A* getObject()
{
static struct A *instance = NULL;
// do lock here
if(instance == NULL)
{
instance = malloc(sizeof(*instance));
instance->a = 1;
instance->b = 2;
}
// do unlock
return instance;
};
请注意,您还需要一个功能来释放单身人士。特别是如果它抓取了在进程退出时未自动释放的任何系统资源。
答案 3 :(得分:5)
C中的单身人士会非常奇怪。 。 。我从来没有见过一个看起来特别优雅的“面向对象的C”的例子。如果可能,请考虑使用C ++。 C ++允许您选择要使用的功能,很多人只是将它用作“更好的C”。
下面是一个非常典型的无锁一次性初始化模式。如果前一个为null,则InterlockCompareExchangePtr以新的值原子交换。这可以保护多个线程同时尝试创建单例,只有一个会获胜。其他人将删除他们新创建的对象。
MyObj* g_singleton; // MyObj is some struct.
MyObj* GetMyObj()
{
MyObj* singleton;
if (g_singleton == NULL)
{
singleton = CreateNewObj();
// Only swap if the existing value is null. If not on Windows,
// use whatever compare and swap your platform provides.
if (InterlockCompareExchangePtr(&g_singleton, singleton, NULL) != NULL)
{
DeleteObj(singleton);
}
}
return g_singleton;
}
DoSomethingWithSingleton(GetMyObj());
答案 4 :(得分:2)
这是另一个视角:C程序中的每个文件实际上都是一个单例类,它在运行时自动实例化,不能被子类化。
extern
声明它们。)为所有内容添加正确的前缀,现在您可以使用my_singleton_method()
代替my_singleton.method()
。
如果你的单例是复杂的,你可以在使用之前编写一个generate_singleton()
方法来初始化它,但是你需要确保所有其他的公共方法检查它是否被调用,如果没有则错误输出。
答案 5 :(得分:0)
我认为这个解决方案对于大多数用例来说可能是最简单和最好的...
在这个例子中,我创建了一个单实例全局调度队列,如果你要跟踪来自多个对象的调度源事件,你肯定会这样做;在这种情况下,当新任务添加到队列时,每个监听队列事件的对象都会收到通知。一旦设置了全局队列(通过 queue_ref()
),就可以在包含头文件的任何文件中使用 queue
变量引用它(下面提供了示例)。
在我的一个实现中,我在 AppDelegate.m 中调用了 queue_ref()
(main.c 也可以)。这样,queue
将在任何其他调用对象尝试访问它之前被初始化。在其余对象中,我只是简单地调用了 queue
。从变量中返回值比调用函数要快得多,然后在返回之前检查变量的值。
在 GlobalQueue.h 中:
#ifndef GlobalQueue_h
#define GlobalQueue_h
#include <stdio.h>
#include <dispatch/dispatch.h>
extern dispatch_queue_t queue;
extern dispatch_queue_t queue_ref(void);
#endif /* GlobalQueue_h */
在 GlobalQueue.c 中:
#include "GlobalQueue.h"
dispatch_queue_t queue;
dispatch_queue_t queue_ref(void) {
if (!queue) {
queue = dispatch_queue_create_with_target("GlobalDispatchQueue", DISPATCH_QUEUE_SERIAL, dispatch_get_main_queue());
}
return queue;
}
使用:
#include "GlobalQueue.h"
在任何 Objective-C 或 C 实现源文件中。queue_ref()
以使用调度队列。调用 queue_ref()
后,可以通过所有源文件中的 queue
变量使用队列示例:
调用 queue_ref():
dispatch_queue_t serial_queue_with_queue_target = dispatch_queue_create_with_target("serial_queue_with_queue_target", DISPATCH_QUEUE_SERIAL, **queue_ref()**);
呼叫队列:
dispatch_queue_t serial_queue_with_queue_target = dispatch_queue_create_with_target("serial_queue_with_queue_target", DISPATCH_QUEUE_SERIAL, **queue**));]
答案 6 :(得分:-2)
只做
void * getSingleTon() {
static Class object = (Class *)malloc( sizeof( Class ) );
return &object;
}
也适用于并发环境。