2

我们正在开发一个带有 C 接口的 C++ 库。在C接口中,有:

  • 一个初始化库的函数(Init()——用给定的参数构造实现功能的对象);
  • 一个取消初始化库的函数(DeInit() — 破坏该对象),以及;
  • 几个只调用对象上相应方法的函数(比如Foo()Bar())。

类本身是线程安全的,但我希望 C 接口(即Init()DeInit()和)Foo()也是Bar()线程安全的。

有一个全局变量——指向类实例的指针。您认为以下哪种方法最好?你会以不同的方式来做吗?

  1. 有一个全局unique_ptr<T>和一个全局读/写锁。Foo()Bar()锁定阅读;Init()并将DeInit()其锁定以进行写入。
  2. 使用信号量来跟踪使用实例的线程数。如果DeInit()被调用,则在信号量上等待,直到其计数为 0。
  3. 使用shared_ptr语义。全局shared_ptr可以更改,但在没有用户之前不会删除对象。这有可能使系统过载,因为可能有多个实例(消耗大量内存)来满足挂起操作的需求,因为 Init() 不会(也不需要)等到挂起的操作完成.

选项 3 似乎最容易使用 C++11 实现。如果我错了或太模糊,请纠正我。

编辑澄清:对 Init() 和 DeInit() 的调用正在更改全局指针,因此应该通过锁定来防止它们同时运行。Foo() 和 Bar() 应该能够同时运行。

编辑说明 C 接口是指实现 C 接口的一组函数。我希望这组函数是线程安全的。

4

3 回答 3

2

通常,如果可能,应避免使用全局变量,甚至是隐藏变量。由于您的类已经是线程安全的,因此通过向用户返回句柄来完全取消锁定。他们最有能力知道该怎么做。然后编写好的文档,让用户决定同步什么以及如何同步。例如:

void *handle = Init(...);
void *value = Foo(handle, ...);
int status = Bar(handle, ...);
DeInit(handle, ...);

如果库用户代码在 C++ 中,则可以将其包装到具有同步访问的 C++ 智能处理程序类中(如果需要)。如果用户以某种方式知道永远不会有并发访问(例如,用户代码可能在单个线程中运行),那么就不需要锁定任何东西。

[编辑:错别字]

于 2013-08-24T13:06:14.530 回答
0

鉴于您想要一个全局变量,这是一个答案:

//Lib.h

//extern "c" stuff

void Init(void);

type1 Foo(void);
type2 Bar(void);

void Deinit(void);


//Lib.cpp
#include "Lib.h"

// includes for mutex etc.

namespace {
    class RealObject{
    public:
        RealObject(){}
        type1 Foo();
        type2 Bar();
    };
    RealObject* getObject(){
        static RealObject obj;
        return &obj;
    }
    Mutex mutex;
}

void Init(void){
    Lock lock(mutex);
    getObject();
}

type1 Foo(void){
    return getObject()->Foo();
}
type2 Bar(void){
    return getObject()->Bar();
}

void Deinit(void){}

这假设所有客户端总是Init()在任何组合之前调用FooBar

第一个Init()创建全局实例并且Deinit()是一个 nop。

互斥锁的存在只是为了制作一个单例。

于 2013-08-23T15:24:43.700 回答
0

如果我正确理解你的问题。

您可以通过在您的 c++ 类上使用静态互斥锁/CS 来做到这一点,这将阻止外部线程同时运行任何内部函数

您的问题使我感到困惑,因为我看不到接口如何是线程安全的。只要接口不执行任何逻辑,它将依赖于通过它运行的代码。

如果您的界面正在执行一些与线程相关的逻辑,那么也许您应该考虑将该逻辑移动到库端,在那里您可以更好地控制代码中发生的其他事情。

如果此答案与您的问题完全无关,请提前道歉。

于 2013-08-23T11:26:21.757 回答