【问题标题】:Thread safe destruction of Read-Write Lock in CC中读写锁的线程安全销毁
【发布时间】:2015-01-20 15:44:32
【问题描述】:

我正在尝试使用 POSIX 信号量在 C 中编写线程安全的读写锁。可以看到源代码here的当前状态。 我跟着this创建了一个读者首选的锁。

问题是我想在调用 rwl_destroy() 时为任何可能的状态处理锁的销毁。

如果调用了destroy并且没有其他线程在锁上,那么它将锁定wrt(由writers使用)以防止任何其他线程访问由锁保护的数据。接下来,destroy 函数应该销毁信号量并释放为 ReadWriteLock 结构分配的内存。但是如果另一个线程现在正在等待锁怎么办?根据文档,该线程将处于未定义状态。

这就是我试图避免的,以便让锁更易于使用。

编辑:

当前代码是:

typedef struct ReadWriteLock
{
sem_t wrt;
sem_t mtx;
sem_t delFlag;
int readcount;
int active;
}ReadWriteLock;

//forward declaration
/* This function is used to take the state of the lock.
 * Return values:
 *      [*] 1 is returned when the lock is alive.
 *      [*] 0 is returned when the lock is marked for delete.
 *      [*] -1 is returned if an error was encountered.
 */
int isActive(ReadWriteLock*);

int rwl_init(ReadWriteLock* lock)
{
lock = malloc(sizeof(ReadWriteLock));
if (lock == NULL)
{
    perror("rwl_init - could not allocate memory for lock\n");
    return -1;
}
if (sem_init(&(lock->wrt), 0, 1) == -1)
{
    perror("rwl_init - could not allocate wrt semaphore\n");
    free(lock);
    lock = NULL;
    return -1;
}
if (sem_init(&(lock->mtx), 0, 1) == -1)
{
    perror("rwl_init - could not allocate mtx semaphore\n");
    sem_destroy(&(lock->wrt));
    free(lock);
    lock = NULL;
    return -1;
}
if (sem_init(&(lock->delFlag), 0 , 1) == -1)
{
    perror("rwl_init - could not allocate delFlag semaphore\n");
    sem_destroy(&(lock->wrt));
    sem_destroy(&(lock->mtx));
    free(lock);
    lock = NULL;
    return -1;
}

lock->readcount = 0;
lock->active = 1;
return 0;
}

int rwl_destroy(ReadWriteLock* lock)
{
errno = 0;
if (sem_trywait(&(lock->wrt)) == -1)
    perror("rwl_destroy - trywait on wrt failed.");
if ( errno == EAGAIN)
    perror("rwl_destroy - wrt is locked, undefined behaviour.");

errno = 0;
if (sem_trywait(&(lock->mtx)) == -1)
    perror("rwl_destroy - trywait on mtx failed.");
if ( errno == EAGAIN)
    perror("rwl_destroy - mtx is locked, undefined behaviour.");

if (sem_destroy(&(lock->wrt)) == -1)
    perror("rwl_destroy - destroy wrt failed");
if (sem_destroy(&(lock->mtx)) == -1)
    perror("rwl_destroy - destroy mtx failed");
if (sem_destroy(&(lock->delFlag)) == -1)
    perror("rwl_destroy - destroy delFlag failed");

free(lock);
lock = NULL;
return 0;
}

int isActive(ReadWriteLock* lock)
{
errno = 0;
if (sem_trywait(&(lock->delFlag)) == -1)
{
    perror("isActive - trywait on delFlag failed.");
    return -1;
}
if ( errno == EAGAIN)
{//delFlag is down, lock is marked for delete
    perror("isActive - tried to lock but ReadWriteLock was marked for delete");
    return 0;
}
return 1;
}

我也有这些功能:

int rwl_writeLock(ReadWriteLock*);

int rwl_writeUnlock(ReadWriteLock*);

int rwl_readLock(ReadWriteLock*);

int rwl_readUnlock(ReadWriteLock*);

所以我的问题是如何更改这些函数以避免我上面描述的未定义状态。在尝试销毁 ReadWriteLock 之前,甚至有可能还是该代码的用户应该负责释放所有锁?

isActive() 函数和 delFlag 信号量目前没有使用,它们是我试图解决问题的。

【问题讨论】:

  • 请在问题中包含您遇到问题的代码。如果链接失效,问题将在没有上下文的情况下留在此处。
  • 这是一个生命周期管理问题。如果程序在对象被销毁后使用它,这通常称为错误。
  • @xxbbcc 我可以发布代码,但我认为这不会有帮助,这个问题更具理论性。我会更新我的问题。
  • 我不认为ReadWriteLock*rwl_init 的正确类型——rwl_init 中的指针malloced 在函数返回后实际上丢失了。你可能更想要ReadWriteLock**(和*lock = malloc(...),等等)。同样地,rwl_destroy 中的lock = NULL 没有持久的效果。
  • @Dirk 感谢您指出这一点,我正在纠正它。关于锁的破坏问题仍然存在。

标签: c multithreading posix destroy readwritelock


【解决方案1】:

您应该实现 ReadWriteLock 实例的“已处置”状态(“活动”字段看起来很合适,但您不使用它,为什么?)。

在 rwl_writeLock / rwl_readLock 中检查两次,在 sem_wait() 调用之前和之后。这个技巧被称为“双重检查锁定模式”。如果您在进入 sem_wait 之前发现您的 Lock 已被删除,请离开该功能。 如果你进入sem_wait后发现你的Lock被删除了,立即做sem_post并离开。

在您的 destroy() 例程中,设置 active=0,然后将 sem_post 设置为两个信号量(如果 sem_post 失败,请不要打扰)。如果之后您仍然需要 sem_destroy,请睡一会(以便所有读者和作者都有时间接收信号)并执行 sem_destroy。

附:如果您确定信号量不再使用,则实际上无需调用 sem_destroy。

【讨论】:

  • 与 sleep() 同步是一种广泛认可的反模式。
  • @NickZavaritsky:rwlock 的自写实现,旨在在堆中动态分配并在任意时刻销毁......是的,这是关心反模式的正确地方.你真的猜到 OP 有一个很好的终结例程,可以让他同步销毁工作线程吗?...好吧,我不应该问...
  • @NickZavaritsky 我明白你为什么将 sleep() 的使用称为反模式,但你有什么建议呢?我仍然想不出 Yury 的建议有什么问题,但我坚持看其他意见。
  • @JasonPap 通过实现终结例程可以避免使用睡眠。如果你有一个终结例程,它在所有工作线程停止后执行,你可以将“rwlock_destroy()”方法分成两个:rwlock_close 和 rwlock_destory。然后在close中设置active=0并在destroy中调用sem_destroy。但是你需要加入工作线程才能做到这一点。
  • @YuryRumega 我不想加入任何线程,因为它们不是由这个 API 结构创建的。线程可能也在为用户做一些其他的工作,所以我不能决定结束它们。
猜你喜欢
  • 2015-05-24
  • 1970-01-01
  • 2012-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-08
  • 1970-01-01
相关资源
最近更新 更多