linux中多线程同步对象

linux中多线程c++编程同步对象的使用

对于多线程程序来说,同步是指在同一时刻只有有个线程能够访问某一公共资源;在linux中可以使用互斥锁(mutex)、条件变量(condtion varirable)、读写锁(reader-writer-lock)和信号量(semphore)来实现资源同步。

1、mutex

mutex简介及使用:
mutex的初始化包括静态和动态初始化两种,静态初始化使用 PTHREAD_MUTEX_INITIALIZER
常量即可初始化,动态初始化使用 pthread_mutex_ini函数初始化,使用完之后需要使用
pthread_mutex_destory释放;使用 pthread_mutex_lock 对需要同步的资源加锁,使用
pthread_mutex_unlock 释放锁。
例:
//静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//动态初始化
pthread_mutex_t mutex;
pthread_mutex_ini(&mutex, NULL);
//加锁
pthread_mutex_lock(&mutex);

//解锁
pthread_mutex_unlock(&mutex);

/*
 * 模拟4个窗口出售20张票,如果不加锁;可能就会出现售出的票比实际的票多的情况
 */

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>


int total_ticket_num = 20;
pthread_mutex_t mutex_x = PTHREAD_MUTEX_INITIALIZER;

void *sell_ticket(void*)
{
    for (int i = 0; i < 20; i++)
    {
        pthread_mutex_lock(&mutex_x);
        if (total_ticket_num > 0)
        {
            sleep(1);
            printf("sell the %dth ticket\n", 20 - total_ticket_num + 1);
            total_ticket_num--;
        }
        pthread_mutex_unlock(&mutex_x);
    }
    return 0;
}

int main()
{
    pthread_t tids[4];
    int iret;
    int i;
    for (i = 0; i < 4; i++)
    {
        iret = pthread_create(&tids[i], NULL, sell_ticket, NULL);
        if (iret)
        {
            printf("pthread_create error : %d\n", iret);
            return iret;
        }
    }

    sleep(20);
    void *retval;
    for (i = 0; i < 4; i++)
    {
        iret = pthread_join(tids[i], &retval);
        if (iret)
        {
            printf("pthrad_join error : %d\n", iret);
            return iret;
        }
        printf("the thread : %ld state is : %ld\n", tids[i], (long*)retval);
    }
    return 0;
}

2、condtion variable

condtion variable的初始化和销毁;
//静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//动态初始化
pthread_cond_t cond;
pthread_cond_ini(&cond, NULL);
//销毁
pthread_cond_destory(&cond);
condtion variable其他相关函数简介和使用;条件变量的使用需要和互斥锁相互配合
例:
pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER;
//在使用 pthread_cond_wait 函数使线程进入等待状态前,需先对公共资源加锁
pthread_mutex_lock(&pmutex);

pthread_cond_wait(&pcond, &pmutex);

pthread_mutex_unlock(&pmutex);
pthread_cond_wait 之后的代码不再执行,当接收到来自 pthread_cond_signal 的信号时
将继续执行,如果有多个线程都处于 pthread_cond_wait ; pthrea_cond_signal 只会唤醒
其中的一个线程;唤醒顺序规则:线程优先级高的先被唤醒,优先级相同的,等待时间
长的先被唤醒


/*
 * 条件变量关于出租车应用举例
 */

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

//提示处注册到达的条件变量
pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;
//同步锁
pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER;


void *traveler_arrive(void *args)
{
    printf("Traveler %s needs a taxi now!\n", (char*)args);
    pthread_mutex_lock(&pmutex);
    pthread_cond_wait(&pcond, &pmutex);
    pthread_mutex_unlock(&pmutex);
    printf("Traveler %s now got a taxi!\n", (char*)args);
    pthread_exit((void*)0);
}

void *taxi_arrive(void *args)
{
    printf("%s's taxi arrived!\n", (char*)args);
    pthread_cond_signal(&pcond);
    pthread_exit((void*)0);
}

int main()
{
    pthread_t tids[3];
    int iret;
    iret = pthread_create(&tids[0], NULL, taxi_arrive, (void*)("Jack"));
    if (iret)
    {
        printf("pthread_create error : %d\n", iret);
        return iret;
    }
    printf("Time passing By.\n");
    sleep(1);
    iret = pthread_create(&tids[1], NULL, traveler_arrive, (void*)("SuSan"));
    if (iret)
    {
        printf("pthread_create error : %d\n", iret);
        return iret;
    }
    printf("Time Passing By.\n");
    sleep(1);
    iret = pthread_create(&tids[2], NULL, taxi_arrive, (void*)("Mike"));
    if (iret)
    {
        printf("pthread_create error : %d\n", iret);
        return iret;
    }
    printf("Time Passing By.\n");
    sleep(1);
    void *retval;
    for (int i = 0; i < 3; i++)
    {
        iret = pthread_join(tids[i], &retval);
        if (iret)
        {
            printf("pthread_join error : %d\n", iret);
            return iret;
        }
        printf("The thread : %lu exit code is : %ld\n", tids[i], *(long*)retval);
    }
    return 0;
}

/*
 * 程序分析:
 * 实例共有2个线程函数:旅客、出租车分别用来表示旅客到达和出租车到达;
 * 首先创建一个出租车的线程表示Jack的出租车已经到达,处于等乘客的状态;
 * 在创建一个旅客线程SuSan,SuSan到达后需要乘坐出租车;最后在创建一个
 * 出租车的线程Mike。最总结果,SuSan乘坐出租车离开;但是我们并不知道,
 * SuSan是乘坐的谁的出租车离开的,还有就是SuSan到的时候Jack的出租车已经
 * 在等待了但是SuSan为什么不坐。其实造成这两个问题的根本原因是,驾驶
 * 出租车的驾驶员只会在出租车到的时候喊一嗓子(“×××驾驶的出租车到了,
 * 有没有需要乘车的,之后就再也不会主动去招揽乘客了,出租车驾驶员可能
 * 比较懒不愿意多说话”),还有一个原因是乘客都是不会说话呢的盲人,只能
 * 听见。所以要解决让Jack、SuSan不要白等的问题就需要,就需要让出租车驾
 * 驶员变得勤快一点(无论有没有乘客需要乘车,都要一直吆喝有没有乘客需要
 * 乘车,直到有乘客乘坐他的出租车为止)。下面是一个勤快的出租车驾驶员
 */


int traveler_count = 0;

void *advance_traveler_arrire(void *args)
{
    printf("Traveler %s needs a taxi now!\n", (char*)args);
    pthread_mutex_lock(&pmutex);
    ++traveler_count;
    pthread_cond_wait(&pcond, &pmutex);
    pthread_mutex_unlock(&pmutex);
    printf("Traveler %s now got a taxi!\n", (char*)args);
    pthread_exit((void*)0);
    pthread_exit((void*)0);
}

void *advance_taxi_arrire(void *args)
{
    printf("%s's taxi arrived!\n", (char*)args);
    while (1)
    {
        if (traveler_count > 0)
        {
            pthread_mutex_lock(&pmutex);
            --traveler_count;
            pthread_mutex_unlock(&pmutex);
            pthread_cond_signal(&pcond);
        }
    }
    pthread_exit((void*)0);
}

/*
 * 其实经过改写后的程序就模拟了生产者和消费者
 */

3、reader-writer-lock

读写锁的特性和应用场景:
(1)读写锁比起互斥锁具有更高的实用性与并行性,可以有多个线程同时占用读模式
的读写锁,但是只能有一个想成占用写模式的读写锁,读写搜的3中状态如下所述。
1)当读写时写加锁状态时,这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻
塞。
2)当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都是可以得到访问
权限的,但是以写模式对它进行加锁的线程将会被阻塞。
3)当读写锁在读模式的所状态时,如果有另外的线程试图以写模式加锁,读写锁通常会
阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁的请求
则长期阻塞。

处理读者–写者问题的两种常见策略是强读者同步和强写者同步。在强读者同步中,总是
给读者更高的优先权,只要写者当前没有进行写操作,读者就可以获得访问权限;而在强
写者同步中,则往往将优先权交付給写者,而读者只能等待所有正在等待的或者正在执行
的写者结束以后才能执行。关于读者–写者模型,由于读者往往要求查看最新的信息记录,
例如航班订票系统往往会使用强写者同步策略,而图书馆查阅系统则采用前读者同步策略。

(2)读写锁机制是由POSIX提供的,如果写者没有持有写锁,那么所有的读者都可以持有
这把锁,而一旦有某个写者阻塞在上锁的时候,那么就由POSIX系统决定是否允许读者获取
该锁。
读写锁相关的函数使用,如下所述。
1)初始化和销毁读写锁
//静态分配
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
//动态分配
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
//销毁
pthread_rwlock_destory(&rwlock);

//获取读出锁,如果相应的读出锁已经被写者占用,那么就阻塞调用线程
pthread_rwlock_rdlock(&rwlock);
//获取一个写者锁,如果相应的写入锁已经被其他的写者或读者占用,那么就阻塞该线程
pthread_rwlock_wrlock(&rwlock);
//释放一个读者或写者锁
pthread_rwlock_unlock(&rwlock);

注:其中读写加锁的函数的另外两个非阻塞函数如下:
pthread_rwlock_tryrdlock(&rwlock);
pthread_rwlock_trywrlock(&rwlock);
这两个函数获得锁返回0,获取锁失败返回EBUSY



#include <stdio.h>
#include <pthread.h>
#include <unistd.h>


pthread_rwlock_t rwlock;
#define THREADNUM 5

void *readers(void *args)
{
    pthread_rwlock_rdlock(&rwlock);
    printf("reader %ld got the lock!\n", (long)args);
    pthread_rwlock_unlock(&rwlock);
    pthread_exit((void*)0);
}


void *writers(void *args)
{
    pthread_rwlock_wrlock(&rwlock);
    printf("writer %ld got the lock!\n", (long)args);
    pthread_rwlock_unlock(&rwlock);
    pthread_exit((void*)0);
}


int main()
{
    int iret, i;
    pthread_t writer_id, reader_id;
    pthread_attr_t attr;
    int nreadercount = 1, nwritercount = 1;
    iret = pthread_rwlock_init(&rwlock, NULL);
    if (iret)
    {
        fprintf(stderr, "init lock failed!\n");
        return iret;
    }
    //初始化线程属性
    pthread_attr_init(&attr);
    //设置线程的分离状态,即调用 pthread_join 不能获取线程的状态码
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    for (i = 0; i < THREADNUM; i++)
    {
        if (i % 3)
        {
            iret = pthread_create(&reader_id, &attr, readers, (void*)nreadercount);
            if (iret == 0)
            {
                printf("create reader thread successed, the thread id is : %lu\n", (unsigned long)reader_id);
                ++nreadercount;
            }
        }
        else
        {
            iret = pthread_create(&writer_id, &attr, writers, (void*)nwritercount);
            if (iret == 0)
            {
                printf("create writer thread successed, the thread is is : %lu\n", (unsigned long)writer_id);
                ++nwritercount;
            }
        }
    }
    sleep(5);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_34511364/article/details/87878413