提出一个用于原子访问共享内存的包装器

Posted

技术标签:

【中文标题】提出一个用于原子访问共享内存的包装器【英文标题】:Coming up with a wrapper for atomically accessing shared memory 【发布时间】:2021-11-23 08:10:36 【问题描述】:

我在这里纠结于多线程概念。

有两个线程访问一个共享结构。想法是先让ThreadTwo 获取锁并将data 设置为true,然后ThreadOne 运行...如果data 设置为true,它将进入睡眠状态,并在@ 之后发出信号后唤醒987654326@ 被设置为false

我所看到的情况:ThreadOne 进入睡眠状态,在 ThreadTwo 禁用 data 之后,两个线程似乎都处于死锁状态,或者更确切地说它们被卡住了。

我的理解是:我在AccessShm(shmInfo, GET)的末尾解锁了互斥锁,我相信pthread_cond_signal需要已经获得锁,因为它在睡觉前解锁,并且曾经从ThreadTwo发出信号,ThreadOne唤醒,获取锁,并尝试在 AccessShm 内再次获取,但不能,因为它已经被获取,因此挂起?

typedef enum

    SET,
    GET
 Task;

typedef struct

    volatile bool data;     
    pthread_mutex_t pMutex;
    pthread_cond_t cv;
 ShmInfo;

bool AccessShm(ShmInfo *pShm, Task t, ...)  

    va_list args;
    bool setValue = true;
    bool retValue = true;

    va_start(args, t);

    pthread_mutex_lock(&pShm->pMutex);

    switch (t)
    
        case SET:
            setValue = va_arg(args, bool);  
            pShm->data = setValue;
        
            if (setValue == false)
            
                pthread_cond_signal(&pShm->cv); // wake up only when pShm->data is disabled
            
            break;
        
        case GET:
            retValue = pShm->data;
            break;

        default:
            break;
    
    pthread_mutex_unlock(&pShm->pMutex);
    return retValue;


void ThreadOne(void *arg)

    ShmInfo *shmInfo = (ShmInfo *) arg;
    while(1)
    
        while(AccessShm(shmInfo, GET) == true) 
        
          pthread_cond_wait(&shmInfo->cv, &shmInfo->pMutex); 
        
        // ...
    


void ThreadTwo(void *arg)

    ShmInfo *shmInfo = (ShmInfo *) arg;
    // ...
    AccessShm(shmInfo, SET, true);
    
    // some work/delay
    AccessShm(shmInfo, SET, false);

【问题讨论】:

【参考方案1】:

可能是因为您没有在AccessShm() 中调用va_end()(这是必需的)吗?

显然不是。

您使用互斥锁和条件变量的结构有点不寻常。通常,当调用pthread_cond_wait() 时,互斥锁会被锁定以确保确定性。

我建议将互斥锁的锁定/解锁从AccessShm() 移到ThreadOne()ThreadTwo() 中。假设互斥锁已锁定,对pthread_cond_signal() 的调用可以留在AccessShm() 中,您可以通过在该函数主体的开头调用assert(pthread_mutex_trylock(&pShm->pMutex)) 来验证这一点。

【讨论】:

是的,这是我想念的东西,但肯定不是答案。你不认为它归结为导致它的锁定流吗?我不完全确定,但试图完全理解...... 这个包装器的想法是原子地访问变量,而不必在应用程序中手动调用互斥锁和解锁,以避免在错过锁定/解锁的情况下造成任何破坏。 pthread_cond_signal 无论如何都必须在获得锁之后调用,所以将锁定机制留在包装器内不是更好吗? @xyf 这是个好主意,除了在调用 pthread_cond_wait() 时应该锁定互斥锁,而且这是在包装器之外完成的——所以你不妨接受必要的东西。【参考方案2】:

在调用 pthread_cond_wait() 之前,您没有锁定与条件变量关联的互斥锁。

必须只调用 pthread_cond_wait() 并锁定关联的互斥锁。

每the POSIX pthread_cond_wait() documentation(加粗我的):

pthread_cond_timedwait()pthread_cond_wait() 函数应阻塞条件变量。 应用程序应确保调用这些函数时调用线程锁定mutex;否则,将导致错误(对于 PTHREAD_MUTEX_ERRORCHECK 和健壮的互斥锁)或未定义的行为(对于其他互斥锁)。

...

【讨论】:

对不起,互斥锁怎么和我的条件变量没有关联?应用中只有一个CV和一个MUTEX @xyf 您的ThreadOne 代码调用AccessShm,它锁定然后解锁互斥体。然后在不持有互斥锁的情况下调用pthread_cond_wait。这是非法的,原因有两个:1)标准是这样说的。 2) 如果对pthread_cond_signal 的调用发生在你调用pthread_mutex_unlock 之后但在你调用pthread_cond_wait 之前会发生什么?你会等待已经发生的事情,可能永远等待。 我明白你的意思,但是pthread_cond_signal 总是在AccessShm 内部获得锁之后调用 @xyf 你的代码不工作,标准说,“你必须做 X”,你在争论为什么你真的不需要做 X?!? 这不是争论,而是试图了解幕后事物的工作原理:) 你认为有可能有一个包装器来做我正在做的事情吗?

以上是关于提出一个用于原子访问共享内存的包装器的主要内容,如果未能解决你的问题,请参考以下文章

访问共享内存时出现分段错误

跨线程共享内存访问

共享内存主/从进程访问单个串口

Java内存模型之原子性问题

可见性原子性和有序性

一次使用多个共享内存实例