互斥量应该存储在 main 中还是对象中?

Should mutexes be stored in main or in the object?

以 C 中的对象为例:

/* object.h */

typedef struct Object Object;

Object* createObject();
void freeObject(Object* object);

int getObjectNumber(Object* object);
void incrementObjectNumber(Object* object);

这是一个非常简单的不透明类型,它存储一个数字并可以递增它。

为了让我的代码线程安全,我有两个选择。第一个是在 Object:

中存储一个互斥量
void
func(Object* object)
{
    incrementObject(object);
}

int
main()
{
    Object* object = createObject();

    Thread thread1 = startThread(func, object);
    Thread thread2 = startThread(func, object);

    waitThread(thread1);
    waitThread(thread2);

    freeObject(object);
}

第二种是在main中存放一个互斥体:

void
func(Object* object, Mutex mutex)
{
    lockMutex(mutex);
    incrementObject(object);
    unlockMutex(mutex);
}

int
main()
{
    Object* object = createObject();

    Mutex mutex;

    Thread thread1 = startThread(func, object, mutex);
    Thread thread2 = startThread(func, object, mutex);

    waitThread(thread1);
    waitThread(thread2);

    freeObject(object);
}

如果只有一个对象,哪一个是更好的做法?

这取决于你想达到什么目的。有时您需要内部线程安全,有时需要外部线程安全。

通常,决定取决于外部用户是否需要组合操作。比如说,他们希望 f()g() 都自动发生。如果您将互斥锁隐藏在 Object 中,如果不自己管理另一个互斥锁,他们将无法实现这一点(然后您必须处理正在使用的多个锁,这可能导致死锁等)。

无论是一个对象还是多个对象,最好将互斥量与不透明对象一起存储。首先,互斥锁和它保护的数据属于一体。而且,线程安全需要由您的 "ADT" 而不是调用者来处理。只能通过 setters/getters 访问对象,这将在内部处理线程安全。在这种情况下 incrementObject.

这样你就可以声明你的整个库是线程安全的,它会照顾好自己而不是把责任推给别人。您的第一个示例更清晰 API,而第二个示例要求调用者将互斥锁拖到用户定义的回调中,这不是一个干净的解决方案。