POSIX进程同步(一):互斥锁和条件变量

本文围绕C++多进程同步展开,介绍了互斥量和条件变量。它们出自Posix.1线程标准,可用于线程和进程间同步。说明了其初始化分静态和动态,进程间同步用动态且需放共享内存区并设属性为共享。还提及操作步骤及生产者消费者流程,最后给出代码示例。

前言

对于进程间的同步知识,之前觉的自己什么都了解一些,在使用的时候觉的什么都不确定。所以决定好好温故一下。

互斥量和条件变量出自Posix.1线程标准,一般用于线程间的同步,但是它也可以用作进程间的同步。

是否能用于进程间同步在posix中是一个选项,不是必然的要求。可以通过如下代码测试是否支持进程间同步

 #ifdef _POSIX_THREAD_PROCESS_SHARED
       //支持进程间同步
 #endif

一、互斥量和条件变量的初始化

互斥量和条件变量的结构体如下:

pthread_mutex_t mutex;
pthread_cond_t cond;

它们的初始化步骤基本一样,都分为静态初始化和动态初始化。在线程间同步一般用静态初始化,在进程间同步用动态初始化。

//静态初始化
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

//动态初始化
pthread_mutex_init();
pthread_cond_init();

要想用作进程间同步,需要将这个两个东东放在进程都能访问的公共区域,所以需要放在共享内存区。这样多个进程能够访问,起到同步共享内存的作用。

并且pthread_mutex_t 和 pthread_cond_t是有属性的,默认属性是用作线程的,用于进程间需要设置它们的属性为共享,代码片段如下:

pthread_mutex_t mutex;
pthread_cond_t cond;

void Init(){
	//定义条件变量属性,并初始化
	pthread_condattr_t condattr;
	pthread_condattr_init(&condattr);
	
	//定义互斥量属性,并初始化
	pthread_mutexattr_t mutexattr;
	pthread_mutexattr_init(&mutexattr);

	//判断系统是否支持用于进程间同步,并且设置它们的属性为进程间使用
#ifdef _POSIX_THREAD_PROCESS_SHARED
	pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
	pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);
#else
        //ERROR does not support PTHREAD_PROCESS_SHARED
#endif

	//使用已经初始化好的属性变量来初始化互斥量和条件变量
	pthread_cond_init(&cond, &condattr);
	pthread_mutex_init(&mutex, &mutexattr);

	//设置完成后属性变量没用了,销毁
	pthread_mutexattr_destroy(&mutexattr);
	pthread_condattr_destroy(&condattr);
}

二、操作步骤

操作函数的定义如下

#include <pthread.h>

pthread_mutex_lock(pthread_mutex_t*);
pthread_mutex_trylock(pthread_mutex_t*);
pthread_mutex_timedlock(pthread_mutex_t*, const struct timespec *);
pthread_mutex_unlock(pthread_mutex_t*);

pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
pthread_cond_signal(pthread_cond_t*);
pthread_cond_broadcast(pthread_cond_t *);
pthread_cond_timedwait(pthread_cond_t*, pthread_mutex_t*, const struct timespec *);

一般的生产者消费者流程如下:

////>> 生产者
lock
//临界区,操作数据
unlock
signal/broadcast

////>> 消费者
lock
wait
//临界区,操作数据
unlock

如果涉及多生产者消费者需要考虑其他的东西。例如在多生产者单消费者中,如果数据没有被消费,那么下一个生产者生产的数据是否覆盖共享区的数据。

三、代码小例子

demo程序是在共享内存中使用条件变量和互斥量来同步两个进程:

生产者进程 a.cpp

#include "type.h"
#include <iostream>
#include <string>
#include "string.h"


int main(int argc, char *argv[])
{

    if(shm_unlink(key.data()) == -1){
        if(errno == ENOENT)
            std::cout << "not have shm!" << std::endl;
    }

    int fd = shm_open(key.data(), O_CREAT|O_RDWR,  S_IRUSR|S_IWUSR);
    if(fd == -1){
        perror("open shm error");
        return -1;
    }

    struct stat sb;
    fstat(fd, &sb);
    std::cout << sb.st_size << std::endl;
    if(sb.st_size != MEM_SIZE)
        ftruncate(fd, MEM_SIZE);

    void *handle = mmap(nullptr, MEM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(handle == MAP_FAILED){
        perror("mmap error : ");
        return -2;
    }
    close(fd);

    SyncLock *g_mutex = reinterpret_cast<SyncLock*>(handle);
    g_mutex->Init();

    uint32_t *size = reinterpret_cast<uint32_t*>(g_mutex+1);
    *size = 0;
    char *data =  reinterpret_cast<char*>(size+1);

    std::string iput("input 'quit' quit while!!! ");
    std::cout << iput << std::endl;

    while(true){
        std::getline(std::cin, iput);

        g_mutex->Lock();
        *size = static_cast<uint32_t>(iput.size());
        if(*size > MEM_SIZE) continue;

        memcpy(data, iput.data(), *size);

        g_mutex->Unlock();
        g_mutex->NotifyOne();
        
        if(iput == "quit")
            break;
    }

    munmap(handle, MEM_SIZE);
    shm_unlink(key.data());

    return 0;
}

消费者进程 b.cpp

#include <iostream>
#include <thread>

#include "type.h"
#include <string.h>


int main(int argc, char *argv[])
{
    int fd = shm_open(key.data(), O_CREAT|O_RDWR,  S_IRUSR|S_IWUSR);
    if(fd == -1){
        perror("open shm error");
        return -1;
    }

    struct stat sb;
    fstat(fd, &sb);
    std::cout << sb.st_size << std::endl;
    if(sb.st_size != MEM_SIZE)
        ftruncate(fd, MEM_SIZE);


    void *handle = mmap(NULL, MEM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(handle == MAP_FAILED){
        perror("mmap error : ");
        return -2;
    }
    close(fd);


    SyncLock *g_mutex = reinterpret_cast<SyncLock*>(handle);

    uint32_t *size = reinterpret_cast<uint32_t*>(g_mutex+1);
    char *data =  reinterpret_cast<char*>(size+1);


    std::string oput;

    while(true){
        g_mutex->Lock();

        while(*size == 0) g_mutex->Wait();

        oput = std::string(data, *size);
        *size = 0;

        g_mutex->Unlock();

        std::cout << oput << std::endl;

        if(oput == std::string("quit"))
            break;
    }

    munmap(handle, MEM_SIZE);

    return 0;
}

公共头文件 type.h

#ifndef TYPE_H
#define TYPE_H

#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include <string>

const std::string key("POSIX_IPC_TEST_01");

const int MEM_SIZE = 4096;

struct SyncLock{
    pthread_mutex_t mutex;
    pthread_cond_t cond;

    void Init(){
        pthread_condattr_t condattr;
        pthread_condattr_init(&condattr);

        pthread_mutexattr_t mutexattr;
        pthread_mutexattr_init(&mutexattr);

    #ifdef _POSIX_THREAD_PROCESS_SHARED
        pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
        pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);
    #else
        //ERROR does not support PTHREAD_PROCESS_SHARED
    #endif

        pthread_cond_init(&cond, &condattr);
        pthread_mutex_init(&mutex, &mutexattr);

        pthread_mutexattr_destroy(&mutexattr);
        pthread_condattr_destroy(&condattr);
    }

    void Destroy(){
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mutex);
    }

    void Lock(){
        pthread_mutex_lock(&mutex);
    }

    void TryLock(){
        pthread_mutex_trylock(&mutex);
    }

    void LockTime(const struct timespec * time){
        pthread_mutex_timedlock(&mutex, time);
    }

    void Wait(){
        pthread_cond_wait(&cond, &mutex);
    }

    void Unlock(){
        pthread_mutex_unlock(&mutex);
    }

    void NotifyOne(){
        pthread_cond_signal(&cond);
    }

    void NotifyAll(){
        pthread_cond_broadcast(&cond);
    }
};


#endif

工程文件CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(POSIX_IPC)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

add_executable(ch1_a ch1/a.cpp ch1/type.h)
target_link_libraries(ch1_a pthread rt)
add_executable(ch1_b ch1/b.cpp ch1/type.h)
target_link_libraries(ch1_b pthread rt)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值