我在 Linux 上用 C 语言开发的代码有问题。
我的结构如下:
typedef struct _my_sem {
unsigned int valid;
pthread_mutex_t m;
} my_sem;
以及以下功能(不报告错误管理):
void my_sem_init(my_sem *s);
void my_sem_destroy(my_sem *s);
void my_sem_lock(my_sem *s);
void my_sem_unlock(my_sem *s);
void my_sem_init(my_sem *s)
{
pthread_mutex_create(&s->m);
s->valid = 1;
}
void my_sem_destroy(my_sem *s)
{
if (s->valid) {
pthread_mutex_destroy(&s->m);
s->valid = 0;
}
}
void my_sem_lock(my_sem *s) {
if (s->valid)
pthread_mutex_lock(&s->m);
}
void my_sem_unlock(my_sem *s) {
if (s->valid)
pthread_mutex_unlock(&s->m);
}
这段代码存在并发问题。如果有人试图锁定 my_sem 并且,在有人销毁对象的同时,调用将会失败。
如何解决这个并发问题?
最佳答案
您提出的设计存在先有鸡还是先有蛋的问题:成员my_sem.valid
旨在向多个线程指示给定互斥体是否处于有效状态,但是多个线程访问这些互斥体当访问之一是写入并且访问彼此之间没有排序时(例如通过使用互斥体保护访问),成员会产生数据竞争。
此外,您无法通过将 valid
的类型调整为可以原子更新的类型来解决此问题,因为互斥体的初始化/终结和 valid
的更新必须作为一个不可分割的单元来执行,以便您的功能正常工作。这需要使用另一个互斥锁、信号量或一些类似的同步辅助工具。
但是,通过向结构中添加额外的同步对象并不能解决任何问题,因为如果您不能依赖现有互斥锁处于已知状态,那么您也不能依赖新对象的状态。因此,为了挽救这种设计,您需要一个全局互斥体或信号量来保护四个函数中每个函数的主体。
这里有一个替代方案:不要分发未初始化的互斥体,也不要销毁正在(或可能仍在使用)使用的互斥体。如果有必要,您甚至可以使用互斥体本身来保护其是否仍在使用的测试,前提是您构建代码以避免在发现互斥体不在其中后重新测试任何互斥体的使用状态的可能性。使用。这将需要更改互斥体的使用方式、互斥体管理函数以及结构体
(如果您完全保留该结构体)。
关于C - 并发问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40001143/