并行处理及分布式系统 第四章 用Pthreads进行共享内存编程


 一、一些概念


        1、在Pthreads程序中,全局变量被所有线程所共享,而在函数中声明的局部变量则(..通常)由执行该函数的线程所私有。

        2、临界区:一个更新共享资源的代码段,一次只允许一个线程执行该代码段。

        3、竞争条件:当多个线程都要访问共享变量或者共享文件这样的共享资源时,如果其中一个

        访问是更新操作,那么这些访问就可能会导致某种错误,我们称之为竞争条件。

        4、互斥量:是互斥锁的简称。是一个特殊类型的变量,通过某些特殊类型的函数,互斥量可

        以用来限制每次只有一个线程能够进入临界区。互斥量保证了一个线程独享临界区,其他线

        程在有线程已经进入临界区的情况下不能同时进入。

        互斥量的特殊类型为:pthread_mutex_t。在使用该类型变量之前必须有系统对其进行初始

        化。

        5、生产者—消费者同步模型:一个线程需要等待另一个线程执行某种操作的同步方式。

        6、路障:通过保证所有线程在程序中处于同一个位置来同步线程,这个同步点称为路障,只

        有所有线程都抵达此路障,线程才能继续运行下去,否则会阻塞在路障处。

        7、条件变量:是一个数据对象,允许线程在某个特定条件或时间发生前都处于挂起状态,当

        时间或条件发生时,另一个线程可以通过信号来唤醒挂起的线程。一个条件变量总是与一个

        互斥量相关联。

        8、Pthreads线程库中的条件变量类型为Pthread_cond_t。

        9、读写锁

                ·提供了两个锁函数,第一个为读操作对读写锁加锁,第二个为写操作对读写锁加锁。

                ·多个线程能够通过调用读锁函数而同时获得锁,但只有一个线程能通过写锁函数获得

                锁。

                ·任何线程拥有了读锁,则任何请求写锁的线程将被阻塞在写锁函数的调用上。

                ·任何线程拥有了写锁,则任何想获取读锁或写锁的线程将阻塞在他们对应的锁函数上。

10、缓存行(块)

                如果一个处理器需要访问主存位置x,那么就不只是将x的内容传入(出)主存,而是将

                一块包含x的内存块传入(出)主存,这样的一块内存称为缓存行(块)。

二、Pthreads编程代码


        编译程序

$ gcc -g -Wall -o 文件名 文件名.c -lpthread

        运行程序

$ ./文件名 <number of thread>
//number of thread 是可选项,表示要启动的线程的个数


        将字符串转换为long int(长整型)

long strtol(const* char number_p  /*in*/,char** end_p  /*out*/,int base  /*in*/);
//number_p是待转换的字符串
//base是表达这个整数值所用的基
//如果end_p不是NULL,它就指向number_p字符串中第一个无效字符(非数值字符)


        生成线程的函数

int pthread_create(pthread_t* thread_p  /*out*/,const pthread_attr_t* attr_p  /*in*/,
                    void* (*start_routine)(void*)  /*in*/,void* arg_p  /*in*/);
//thread_p是一个指针,指向对应的pthread_t对象,她不是由pthread_create函数分配的,必须在调用pthread_create函数前将pthread_t对象分配到内存空间。
//attr_p不用,只是在函数调用时把NULL传递给参数
//(*start_routine)(void*)表示该线程将要运行的函数
//arg_p是一个指针,指向传递给start_routine的参数
//返回值一般用于表示线程调用过程中是否有错误

        停止线程

int pthread_join(pthread_t thread  /*in*/,void** ret_val-p  /*out*/);
//thread指向对应的pthread_t对象
//ret_val_p可以接收任意有pthrea_t对象所关联的那个线程产生的返回值


        互斥量初始化函数

int pthread_mutex_init(pthread_mutex_t* mutex_p  /*out*/,
                        const pthread_mutexattr* attr_p  /*in*/);
//第二个参数不使用,赋值为NULL即可

        使用完互斥量之后调用的函数

int pthread_mutex_destroy(pthread_mutex_t* mutex_p  /*in/out*/);


        获得临界区访问时调用的函数

int pthread_mutex_lock(pthread_mutex_t* mutex_p  /*in/out*/);
//调用该函数会使线程等待,直到没有其他线程进入临界区

        线程退出临界区后调用的函数

int pthread_mutex_unlock(pthread_mutex_t* mutex_p  /*in/out*/);
//调用该函数会通知系统该线程已经完成了临界区中代码的执行

        不同信号量函数的语法:信号量不是Pthreads线程库的一部分,所以需要在使用信号量的程

        序开头加头文件#include<semaphore.h>

int sem_init(sem_t* semaphore_p  /*out*/,int shared  /*in*/,unsigned initial_val  /*in*/);
//一般不使用该函数的shared参数,对这个参数只需要传入常数0就可以
int sem_destroy(sem_t* semaphore_p  /*in/out*/);//销毁
int sem_post(sem_t* semaphore_p  /*in/out*/);//解锁
int sem_wait(sem_t* semaphore_p  /*in/out*/);//阻塞

        条件变量的一般使用方法的伪代码:

lock mutex;
if condition has occurred
    signal thread(s);
else {
    unlock the mutex and block;/*when thread is unblocked,mutex is relocked*/
}
unlock mutex;


        解锁一个阻塞的线程的函数

int pthread_cond_signal(pthread_cont_t* cont_var_p  /*in/out*/);


        解锁所有被阻塞的线程的函数

int pthread_cond_broadcast(pthread_cond_t* cond_var_p  /*in/out*/);


        通过互斥量mutex_p来阻塞线程的函数,被阻塞线程想要解锁只有其他线程调用

pthread_cond_signal或者pthread_cond_broadcast来解锁。当线程解锁后,它重新获得互斥量。

int pthread_cond_wait(pthread_cond_t* cond_var_p  /*in/out*/,
                        pthread_mutex_t* mutex  /*in/out*/);
//该函数相当于按顺序执行了以下函数:
pthread_mutex_unlock(&mutex_p);
wait_on_signal(&cond_var_p);
pthread_mutex_lock(&mutex_p);


        用条件变量实现路障

/*Shared*/
int counter=0;    //共享计数器
pthread_mutex_t mutex;    //互斥量
pthread_cond_t cond_var;    //条件变量
...
void* Thread_work(...){
    ...
    /*Barrier*/
    pthread_mutex_lock(&mutex);    //线程获得临界区的访问权
    counter++;    //共享计数器加1,用来计数使用过临界区的线程的个数
    if(counter==thread_count){    //满足了共享计数器的值等于线程个数的条件
        counter==0;    //共享计数器归零开始下一次计数
        pthread_cond_broadcast(&cond_var);    //解锁所有被阻塞的线程
    }else{
        while(pthread_cond_wait(&cond_var,&mutex)!=0);    //通过互斥量来阻塞线程
    }
    pthread_mutex_unlock(&mutex);    //通知系统该线程已经已经完成了临界区中代码的执行
    ...
}
//注意除了调用pthread_cond_broadcast函数外,其他的某些事件也可能将挂起的线程解锁,因此,函数
//pthread_cond_wait一般被放置于while循环内,如果线程不是被pthread_cond_broadcast或
//pthread_cond_signal函数解锁而是被其他事件解除阻塞,那么能检查到pthread_cond_wait函数的返回值不
//为0,被解除阻塞的线程还会再次执行该函数

        条件变量初始化函数

int pthread_cond_init(pthread_cont_t* cond_p  /*out*/,
                        const pthread_condattr_t* cond_attr_p  /*in*/);
//cond_attr_p不使用,调用函数时传递NULL作为参数值

        条件变量销毁函数

int pthread_cond_destroy(pthread_cond_t* cond_p  /*in/out*/);

        用每个链表结点拥有一个互斥量的方法来实现有序链表的Member函数

struct list_node_s{    //链表结点结构
    int data;
    struct list_node_s* next;
    pthread_mutex_t mutex;    //每个链表结点的互斥量
}
int Member(int value){
    struct list_node_s* temp_p;    //定义临时链表
    pthread_mutex_lock(&head_p_mutex);    //获得头结点的使用权
    temp_p=head_p;    
    while(temp_p->next!=NULL&&temp_p->data<value){    
        //遍历寻找要访问的值,因为是有序链表,所以条件可以为temp_p->data<value
        if(temp_p->next!=NULL)
            pthread_mutex_lock(&(temp_p->next->mutex));    //获得当前节点的下一节点的使用权
        if(temp_p==head_p)
            pthread_mutex_unlock(&head_p_mutex);    
            //如果当前节点是头结点释放头结点的使用权,满足条件进入该循环必然会进行这一步
        pthread_mutex_unlock(&(temp_p->mutex));    //释放当前节点的使用权,便于其他线程使用
        temp_p=temp_p->next;    //当前节点后移一位
    }
    if(temp_p==NULL||temp_p->data>value){    //查找失败的处理
        //当前节点为尾结点或者节点的值大于要找的值都表示匹配失败
        if(temp_p==head_p)
            pthread_mutex_unlock(&head_p_mutex);    
            //如果当前节点是头结点(即该链表为空)释放头结点的使用权
        if(temp_p!=UNLL)
            pthread_mutex_unlock(&(temp_p->mutex));    //虽然查找失败还是需要释放使用权
        return 0;
    }else{    //查找成功的处理
        if(temp_p==head_p)
            pthread_mutex_unlock(&head_p_mutex);
            //如果当前节点是头结点(头结点的值就是要找的值)释放头结点的使用权
        pthread_mutex_unlock(&(temp_p->mutex));    //查找成功后也需要释放使用权
        return 1;
    }
}
//该程序看似会有多次的获取使用权与多次的释放使用权,其实只有两次该过程的出现,一次是对头结点的操
//作,一次是对查找结点的操作

//对于头结点,若满足循环条件则会在循环中释放使用权
//若不满足循环条件则头结点的释放会放在后面进行

        读写锁

int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock_p  /*in/out*/);    //读加锁
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock_p  /*in/out*/);    //写加锁
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock_p  /*in/out*/);    //解锁

int pthread_rwlock_init(pthread_rwlock_t* rwlock_p  /*out*/,
                         const pthread_rwlockattr_t* attr_p  /*in*/);    //初始化
//第二个参数不使用,调用时传递NULL

int pthread_rwlock_destroy(pthread_rwlock_t* rwlock_p  /*in/out*/);    //释放读写锁


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值