Linux下使用pthread实现进程锁的注意事项

1. 背景

最近通过共享内存mmap实现多进程的数据共享,使用PTHREAD_PROCESS_SHARED时写错了,导致进程锁不起作用。

2. 接口

接口说明如下,用法与多线程的互斥锁类似,但需要额外设置一下PTHREAD_PROCESS_SHARED

SYNOPSIS
       #include <pthread.h>

       int pthread_mutexattr_getpshared(
                             const pthread_mutexattr_t *restrict attr,
                             int *restrict pshared);
       int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,
                             int pshared);

       Compile and link with -pthread.

DESCRIPTION
       These  functions  get  and  set  the process-shared attribute in a mutex attributes object.  This attribute must be appropriately set to ensure correct, efficient operation of a
       mutex created using this attributes object.

       The process-shared attribute can have one of the following values:

       PTHREAD_PROCESS_PRIVATE
              Mutexes created with this attributes object are to be shared only among threads in the same process that initialized the  mutex.   This  is  the  default  value  for  the
              process-shared mutex attribute.

       PTHREAD_PROCESS_SHARED
              Mutexes  created  with  this attributes object can be shared between any threads that have access to the memory containing the object, including threads in different pro‐
              cesses.

3 代码

实现一个N个进程同时操作一块共享内存的代码,8个进程每个对变量进行val++操作10w次,所以是期望最终结果是80w次就为正确。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <pthread.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <fcntl.h>

#include <assert.h>

#if 1
struct ut_mmap {
    int val;
    pthread_mutex_t mutex;  // must be here !!!!!!!!
};
# define MUTEX &pmap->mutex
#else
struct ut_mmap {
    int val;
};
pthread_mutex_t g_mutex;
# define MUTEX &g_mutex
#endif

/***************************************************************************************
 *   Name: Main
 *   Desc: main entrance
 *  Input: -
 * Output: -
 * Return: int, EXIT_SUCCESS on success;
 *              EXIT_FAILURE on failure;
 * Others: -
 ***************************************************************************************/
int main(int argc, char *argv[])
{
    int res = -1;
    struct ut_mmap *pmap = NULL;
    pthread_mutexattr_t mutexattr;

    {
        int fd = open("ut_mmap.shm", O_CREAT | O_RDWR, 0777);
        (void)ftruncate(fd, sizeof(*pmap));
        pmap = (struct ut_mmap *)mmap(NULL, sizeof(*pmap), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        close(fd);
    }

    /* Reset */
    memset(pmap, 0, sizeof(*pmap));
    pthread_mutexattr_init(&mutexattr);
    pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(MUTEX, &mutexattr);

    const int num = 8;
    const int times = 100000;

    for (int ix = 0; ix < num; ++ix) {
        pid_t pid = fork();
        if (pid == 0) {
            for (int jx = 0; jx < times; jx++) {
                pthread_mutex_lock(MUTEX);
                pmap->val++;  // 变量互斥操作加一
                pthread_mutex_unlock(MUTEX);
            }
            res = 0;
            goto out;
        }
        assert(pid > 0);
    }

    for (int ix = 0; ix < num; ++ix) {
        wait(NULL);
    }

    printf("Result: %d\n", pmap->val);
    assert(pmap->val == num * times);   // !!!!!!!

    pthread_mutex_destroy(MUTEX);

    munmap(pmap, sizeof(*pmap));
    unlink("ut_mmap.shm");

    res = 0;
out:
    printf("[PID:%8lu] Result:\t\t\t\t[%s]\n", (long)getpid(), res ? "Failure" : "Success");
    exit(res ? EXIT_FAILURE : EXIT_SUCCESS);
}

注意这个地方,设置1的时候是正确的写法,设置为0是错误的写法

#if 1
struct ut_mmap {
    int val;
    pthread_mutex_t mutex;  // must be here !!!!!!!!
};
# define MUTEX &pmap->mutex
#else
struct ut_mmap {
    int val;
};
pthread_mutex_t g_mutex;
# define MUTEX &g_mutex
#endif

正确的执行结果如下:

[PID:    2779] Result:                          [Success]
[PID:    2776] Result:                          [Success]
[PID:    2782] Result:                          [Success]
[PID:    2781] Result:                          [Success]
[PID:    2780] Result:                          [Success]
[PID:    2778] Result:                          [Success]
[PID:    2777] Result:                          [Success]
[PID:    2783] Result:                          [Success]
Result: 800000
[PID:    2775] Result:                          [Success]

错误的执行结果就加不到800000了,由于没有设置sleep,更容易出现竞争锁的情况出错

[PID:    2798] Result:                          [Success]
[PID:    2801] Result:                          [Success]
[PID:    2803] Result:                          [Success]
[PID:    2799] Result:                          [Success]
[PID:    2800] Result:                          [Success]
[PID:    2802] Result:                          [Success]
[PID:    2804] Result:                          [Success]
[PID:    2805] Result:                          [Success]
Result: 148835
lock: lock.cc:95: int main(int, char**): Assertion `pmap->val == num * times' failed.
Aborted (core dumped)

4. 结论

  1. 进程间共享内存,pthread_mutex变量需要包含在共享内存中,否则不生效。
  2. 压测多进程需要给大规模数据,增加竞争可以容易出效果来。
  3. mmap配合文件锁fcntl也可以得到类似效果,但性能比不上pthread。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值