第三十九章 POSIX信号量与互斥锁

POSIX信号量相关函数

sem_open

功能:
    initialize and open a named semaphore
原型:
    sem_t *sem_open(const char *name, int oflag);
参数:
    name : 信号量的名字
    oflag : 
返回值:
    成功 : 返回新信号量的地址
    失败 : SEM_FAILED errno

Link with -pthread

sem_close

功能:
    close a named semaphore
原型:
    int sem_close(sem_t *sem);
参数:
    sem : 信号量
返回值:
    成功 : 0
    失败 : -1 errno

Link with -pthread
功能:
    remove a named semaphore
原型:
    int sem_unlink(const char *name);
参数:
    name : 信号量
返回值:
    成功 : 0
    失败 : -1 errno

Link with -pthread

sem_init

功能:
    initialize an unnamed semaphore
原型:
    int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
    sem : 信号量
    pshared : 指示此信号量是在进程的线程之间还是在进程之间共享。
        0     信号量在进程的线程之间共享,并且应位于所有线程可见的某个地址处(例如,全局变量或在堆上动态分配的变量)。
        非零   则信号量在进程之间共享,并且应位于共享内存的区域中,任何可以访问共享的进程内存区域可以使用sem_post(3),sem_wait(3)等对信号量进行操作。
    value : 初始值
返回值:
    成功 : 0
    失败 : -1 errno
注意:
    虽然初始化的是一个匿名的信号量,它也可以用于不同进程间的多个线程间通信,取决于pshared非0和sem存放在共享内存中
Link with -pthread

sem_destroy

功能:
    destroy an unnamed semaphore
原型:
    int sem_destroy(sem_t *sem);
参数:
    sem : 信号量
返回值:
    成功 : 0
    失败 : -1 errno

Link with -pthread

sem_wait

功能:
    lock a semaphore
原型:
    int sem_wait(sem_t *sem);
参数:
    sem : 信号量
返回值:
    成功 : 0
    失败 : -1 errno

Link with -pthread

sem_post

功能:
    unlock a semaphore
原型:
    int sem_post(sem_t *sem);
参数:
    sem : 信号量
返回值:
    成功 : 0
    失败 : -1 errno

Link with -pthread

POSIX互斥锁相关函数

pthread_mutex_init

功能:
    initialize a mutex
原型:
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
参数:
    mutex : 互斥锁
    attr : 指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁
返回值:
    成功 : 0
    失败 : 返回对应的错误码

pthread_mutex_lock

功能:
    lock a mutex
原型:
    int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:
    mutex : 互斥锁
返回值:
    成功 : 0
    失败 : 返回对应的错误码

pthread_mutex_unlock

功能:
    lock a mutex
原型:
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:
    mutex : 互斥锁
返回值:
    成功 : 0
    失败 : 返回对应的错误码

pthread_mutex_destroy

功能:
    destroy a mutex
原型:
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:
    mutex : 互斥锁
返回值:
    成功 : 0
    失败 : 返回对应的错误码

生产者消费者问题

pctest.c

#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>

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

#include <semaphore.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

#define CONSUMERS_COUNT 1 //消费者线程的个数
#define PRODUCERS_COUNT 5 //生产者线程的个数


#define BUFFSIZE 10     //缓冲区的大小
int g_buffer[BUFFSIZE]; //存放产品的ID的缓冲区

unsigned short in = 0;         //产品的生产位置
unsigned short out = 0;        //产品的消费位置
unsigned short produce_id = 0; //当前正在生产的产品的id
unsigned short consume_id = 0; //当前正在消费的产品的id

sem_t g_sem_full;  //buffer满的信号量
sem_t g_sem_empty; //buffer空的信号量
pthread_mutex_t g_mutex;

pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT]; //总线程数

void *consume(void *arg)
{
    int i;
    int num = (int)arg;
    while (1)
    {
        printf("%d waiting buffer not empty\n", num);
        sem_wait(&g_sem_empty);
        pthread_mutex_lock(&g_mutex);

        for (i = 0; i < BUFFSIZE; ++i)
        {
            printf("%02d ", i);
            if (g_buffer[i] == -1)
                printf("%s", "null");
            else
                printf("%d ", g_buffer[i]);

            if (i == out)
            {
                printf("\t <-- consume");
            }

            printf("\n");
        }

        consume_id = g_buffer[out];
        printf("%d begin consume producet %d \n", num, consume_id);
        g_buffer[out] = -1;
        out = (out + 1) % BUFFSIZE;
        printf("%d end consume producet %d \n", num, consume_id++);

        pthread_mutex_unlock(&g_mutex);
        sem_post(&g_sem_full);
        sleep(1);
    }

    return NULL;
}

void *produce(void *arg)
{
    int i;
    int num = (int)arg;
    while (1)
    {
        printf("%d waiting buffer not full\n", num);
        sem_wait(&g_sem_full);
        pthread_mutex_lock(&g_mutex);

        for (i = 0; i < BUFFSIZE; ++i)
        {
            printf("%02d ", i);
            if (g_buffer[i] == -1)
                printf("%s", "null");
            else
                printf("%d ", g_buffer[i]);

            if (i == in)
            {
                printf("\t <-- produce");
            }

            printf("\n");
        }

        printf("%d begin produce producet %d \n", num, produce_id);
        g_buffer[in] = produce_id;
        in = (in + 1) % BUFFSIZE;
        printf("%d end produce producet %d \n", num, produce_id++);

        pthread_mutex_unlock(&g_mutex);
        sem_post(&g_sem_empty);
        sleep(5);
    }

    return NULL;
}

int main()
{

    int i;
    for (i = 0; i < BUFFSIZE; ++i)
    {
        g_buffer[i] = -1;
    }

    sem_init(&g_sem_full, 0, BUFFSIZE);
    sem_init(&g_sem_empty, 0, 0);

    pthread_mutex_init(&g_mutex, NULL);

    for (i = 0; i < CONSUMERS_COUNT; ++i)
    {
        pthread_create(&g_thread[i], NULL, consume, (void *)i);
    }

    for (i = 0; i < PRODUCERS_COUNT; ++i)
    {
        pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);
    }

    for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++i)
    {
        pthread_join(g_thread[i], NULL);
    }

    sem_destroy(&g_sem_full);
    sem_destroy(&g_sem_empty);
    pthread_mutex_destroy(&g_mutex);

    return 0;
}

自旋锁

  • 自旋锁类似于互斥锁,它的性能比互斥锁更高
  • 自旋锁与互斥锁很重要的一个区别是,线程在申请自旋锁的时候,线程不会被挂起,它处于忙等待的状态

pthread_spin_init

功能:
    initialize a spin lock object
原型:
    int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
参数:
    lock : 自旋锁
    pshared : 
        PTHREAD_PROCESS_SHARED   允许任何有权访问分配了旋转锁的内存的线程对旋转锁进行操作,即使已分配该旋转锁在多个进程共享的内存中
        PTHREAD_PROCESS_PRIVATE  只能由在与初始化旋转锁的线程相同的进程中创建的线程操作

返回值:
    成功 : 0
    失败 : 返回错误编号

pthread_spin_destroy

功能:
    destroy a spin lock object
原型:
    int pthread_spin_destroy(pthread_spinlock_t *lock);
参数:
    lock : 自旋锁
返回值:
    成功 : 0
    失败 : 返回错误编号

pthread_spin_lock

功能:
    lock a spin lock object
原型:
    int pthread_spin_destroy(pthread_spinlock_t *lock);
参数:
    lock : 自旋锁
返回值:
    成功 : 0
    失败 : errno

pthread_spin_unlock

功能:
    unlock a spin lock object
原型:
    int pthread_spin_unlock(pthread_spinlock_t *lock);
参数:
    lock : 自旋锁
返回值:
    成功 : 0
    失败 : errno

spinlockvsmutex1.cc

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <sys/time.h>
#include <list>
#include <pthread.h>
 
#define LOOPS 50000000
 
using namespace std;
 
list<int> the_list;
 
#ifdef USE_SPINLOCK
pthread_spinlock_t spinlock;
#else
pthread_mutex_t mutex;
#endif
 
//Get the thread id
pid_t gettid() { return syscall( __NR_gettid ); }
 
void *consumer(void *ptr)
{
    int i;
 
    printf("Consumer TID %lun", (unsigned long)gettid());
 
    while (1)
    {
#ifdef USE_SPINLOCK
        pthread_spin_lock(&spinlock);
#else
        pthread_mutex_lock(&mutex);
#endif
 
        if (the_list.empty())
        {
#ifdef USE_SPINLOCK
            pthread_spin_unlock(&spinlock);
#else
            pthread_mutex_unlock(&mutex);
#endif
            break;
        }
 
        i = the_list.front();
        the_list.pop_front();
 
#ifdef USE_SPINLOCK
        pthread_spin_unlock(&spinlock);
#else
        pthread_mutex_unlock(&mutex);
#endif
    }
 
    return NULL;
}
 
int main()
{
    int i;
    pthread_t thr1, thr2;
    struct timeval tv1, tv2;
 
#ifdef USE_SPINLOCK
    pthread_spin_init(&spinlock, 0);
#else
    pthread_mutex_init(&mutex, NULL);
#endif
 
    // Creating the list content...
    for (i = 0; i < LOOPS; i++)
        the_list.push_back(i);
 
    // Measuring time before starting the threads...
    gettimeofday(&tv1, NULL);
 
    pthread_create(&thr1, NULL, consumer, NULL);
    pthread_create(&thr2, NULL, consumer, NULL);
 
    pthread_join(thr1, NULL);
    pthread_join(thr2, NULL);
 
    // Measuring time after threads finished...
    gettimeofday(&tv2, NULL);
 
    if (tv1.tv_usec > tv2.tv_usec)
    {
        tv2.tv_sec--;
        tv2.tv_usec += 1000000;
    }
 
    printf("Result - %ld.%ldn", tv2.tv_sec - tv1.tv_sec,
        tv2.tv_usec - tv1.tv_usec);
 
#ifdef USE_SPINLOCK
    pthread_spin_destroy(&spinlock);
#else
    pthread_mutex_destroy(&mutex);
#endif
 
    return 0;
}

svm2.cc

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/syscall.h>
 
#define        THREAD_NUM     2
 
pthread_t g_thread[THREAD_NUM];
#ifdef USE_SPINLOCK
pthread_spinlock_t g_spin;
#else
pthread_mutex_t g_mutex;
#endif
__uint64_t g_count;
 
pid_t gettid()
{
    return syscall(SYS_gettid);
}
 
void *run_amuck(void *arg)
{
       int i, j;
 
       printf("Thread %lu started.n", (unsigned long)gettid());
 
       for (i = 0; i < 10000; i++) {
#ifdef USE_SPINLOCK
           pthread_spin_lock(&g_spin);
#else
               pthread_mutex_lock(&g_mutex);
#endif
               for (j = 0; j < 100000; j++) {
                       if (g_count++ == 123456789)
                               printf("Thread %lu wins!n", (unsigned long)gettid());
               }
#ifdef USE_SPINLOCK
           pthread_spin_unlock(&g_spin);
#else
               pthread_mutex_unlock(&g_mutex);
#endif
       }
        
       printf("Thread %lu finished!n", (unsigned long)gettid());
 
       return (NULL);
}
 
int main(int argc, char *argv[])
{
       int i, threads = THREAD_NUM;
 
       printf("Creating %d threads...n", threads);
#ifdef USE_SPINLOCK
       pthread_spin_init(&g_spin, 0);
#else
       pthread_mutex_init(&g_mutex, NULL);
#endif
       for (i = 0; i < threads; i++)
               pthread_create(&g_thread[i], NULL, run_amuck, (void *) i);
 
       for (i = 0; i < threads; i++)
               pthread_join(g_thread[i], NULL);
 
       printf("Done.n");
 
       return (0);
}

总结

  1. Mutex适合对锁操作非常频繁的场景,并且具有更好的适应性。尽管相比spin lock它会花费更多的开销(主要是上下文切换),但是它能适合实际开发中复杂的应用场景,在保证一定性能的前提下提供更大的灵活度。

  2. spin lock的lock/unlock性能更好(花费更少的cpu指令),但是它只适应用于临界区运行时间很短的场景。而在实际软件开发中,除非程序员对自己的程序的锁操作行为非常的了解,否则使用spin lock不是一个好主意(通常一个多线程程序中对锁的操作有数以万次,如果失败的锁操作(contended lock requests)过多的话就会浪费很多的时间进行空等待)。

  3. 更保险的方法或许是先(保守的)使用 Mutex,然后如果对性能还有进一步的需求,可以尝试使用spin lock进行调优。毕竟我们的程序不像Linux kernel那样对性能需求那么高(Linux Kernel最常用的锁操作是spin lock和rw lock)。

读写锁

  • 只要没有线程持有给定的读写锁用于写,那么任意数目的线程可以持有读写锁用于读
  • 仅当没有线程持有某个给定的读写锁用于读或用于写时,才能分配读写锁用于写
  • 读写锁用于读 称为共享锁, 读写锁用于写 称为排它锁

pthread_rwlock_init

功能:
    initialize a read-write lock object
原型:
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
           const pthread_rwlockattr_t *restrict attr);
参数:
    rwlock : 读写锁指针
    attr : 读写锁属性指针
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_rwlock_destroy

功能:
    destroy a read-write lock object
原型:
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数:
    rwlock : 读写锁指针
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_rwlock_rdlock

功能:
    lock a read-write lock object for reading
原型:
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
参数:
    rwlock : 读写锁指针
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_rwlock_wrlock

功能:
    lock a read-write lock object for writing
原型:
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
参数:
    rwlock : 读写锁指针
返回值:
    成功 : 0
    失败 : 返回错误码

pthread_rwlock_unlock

功能:
    unlock a read-write lock object
原型:
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
参数:
    rwlock : 读写锁指针
返回值:
    成功 : 0
    失败 : 返回错误码

rw_test.cc

#include <iostream>
#include <cstdlib>
 
#include <unistd.h>
#include <pthread.h>
 
using namespace std;
 
struct{
    pthread_rwlock_t rwlock;
    int product;
}sharedData = {PTHREAD_RWLOCK_INITIALIZER, 0};
 
void * produce(void *ptr)
{
    for (int i = 0; i < 5; ++i)
    {
        pthread_rwlock_wrlock(&sharedData.rwlock);
        sharedData.product = i;
        pthread_rwlock_unlock(&sharedData.rwlock);
 
        sleep(1);
    }
}
 
void * consume1(void *ptr)
{
    for (int i = 0; i < 5;)
    {
        pthread_rwlock_rdlock(&sharedData.rwlock);
        cout<<"consume1:"<<sharedData.product<<endl;
        pthread_rwlock_unlock(&sharedData.rwlock);
 
        ++i;
        sleep(1);
    }
}
 
void * consume2(void *ptr)
{
    for (int i = 0; i < 5;)
    {
        pthread_rwlock_rdlock(&sharedData.rwlock);
        cout<<"consume2:"<<sharedData.product<<endl;
        pthread_rwlock_unlock(&sharedData.rwlock);
 
        ++i;
        sleep(1);
    }
}
 
int main()
{
    pthread_t tid1, tid2, tid3;
 
    pthread_create(&tid1, NULL, produce, NULL);
    pthread_create(&tid2, NULL, consume1, NULL);
    pthread_create(&tid3, NULL, consume2, NULL);
 
    void *retVal;
 
    pthread_join(tid1, &retVal);
    pthread_join(tid2, &retVal);
    pthread_join(tid3, &retVal);
 
    return 0;
}

总结

posted @ 2019-10-10 19:25  sfdevs  阅读(212)  评论(0编辑  收藏  举报