LoveStackover的博客

编程小子

多线程之Mutex和Condition Variable实验(三)

实验一:

题目:使用哈希表来组织数据,使4个线程都能够并发的访问,修改哈希表。具体场景:

  • 线程1-3创建一个新的节点,并加入哈希表,且打印该节点的值。
  • 线程4打印整个哈希表。

  实验代码如下所示,笔者绞尽脑汁想出来下面的实现方式,并且兼顾了实验二的内容,也为以后记录read-write_lock作铺垫,为了改善博文的质量,会注意在代码中增加必要的注释。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <err.h>
#include <unistd.h>
#include <stdbool.h>
#define NHASH 3
#define HASH(id) (((unsigned long)id)%NHASH)
struct foo
{
        int f_id;
        struct foo * f_next;
};

struct foo *fh[NHASH];
pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t hashr_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t hashr_cond = PTHREAD_COND_INITIALIZER;
//hashlock负责对hash表的保护,hashr_mutex负责对一种状态的保护
void  foo_printf(struct foo * a)
{
        printf("thread %lu, foo_id:%d\n",pthread_self(),a->f_id);
}
struct foo * foo_alloc(int id)
{
        struct foo * fp;
        int idx;
        if ((fp = malloc(sizeof(struct foo))) != NULL)
        {
                fp->f_id = id;
                foo_printf(fp);
                idx = HASH(id);
                pthread_mutex_lock(&hashlock);
                fp->f_next = fh[idx];
                fh[idx] = fp;
                pthread_mutex_unlock(&hashlock);
        }
        return(fp);
}

struct foo * foo_find(int id)
{
        struct foo * fp;
        pthread_mutex_lock(&hashlock);
        for (fp = fh[HASH(id)]; fp != NULL; fp = fp->f_next)
        {
                if (fp->f_id == id)
                {
                        break;
                }
        }
        pthread_mutex_unlock(&hashlock);
        return(fp);
}

bool foo_rele(int id)
{
        struct foo * fp;
        struct foo * fp1;
        pthread_mutex_lock(&hashlock);
        for (fp = fh[HASH(id)]; fp != NULL; fp = fp->f_next)
        {
                if (fp->f_id == id)
                {
                        if(fp == fh[HASH(id)])
                        {
                                fh[HASH(id)] = fp->f_next;
                        }
                        else
                        {        
                                fp1=fh[HASH(id)];                                                                                                                                                                                      
                                while (fp1->f_next != fp)                                                                                                                                                     
                                        fp1 = fp1->f_next;                                                                                                                                           
                                fp1->f_next = fp->f_next;                                                                                                                                                     
                        }                                                                                                                                                                                              
                        break;                                                                                                                                                                                         
                }                                                                                                                                                                                                      
        }                                                                                                                                                                                                              
        pthread_mutex_unlock(&hashlock);                                                                                                                                                                               
        if(fp == NULL)                                                                                                                                                                                                 
                return 1;                                                                                                                                                                                              
        free(fp);                                                                                                                                                                                                      
        return 0;                                                                                                                                                                                                      
}                                                                                                                                                                                                                      



void * thr_fn(void *arg)                                                                                                                                                                                               
{                                                                                                                                                                                                                                                                                                                                                                                                                     
        if(foo_alloc((int)arg) == NULL)                                                                                                                                                                         
        {                                                                                                                                                                                                              
                errx(1,"foo_alloc error in thread %d\n",pthread_self());                                                                                                                                               
        }                                                                                                                                                                                                              
        else                                                                                                                                                                                                           
        {                                                                                                                                                                                                              
                pthread_mutex_lock(&hashr_mutex);                                                                                                                                                                      
                pthread_cond_wait(&hashr_cond,&hashr_mutex);
/*这里是笔者最为得意的处理,如果在主thread采取pthread_join的方式,
主phtread的执行将依赖三个子进程的结束,而我们无法预测哪个线程最先结
束,所以可能有些线程已经结束,而主进程却白白浪费时间等待其他线程*/                                                                                                                                                          
                if(foo_rele((int)arg) != 0)                                                                                                                                                                            
                {                                                                                                                                                                                                      
                        errx(1,"error in release the foo of the id : %d",(int)arg);                                                                                                                                    
                }                                                                                                                                                                                                      
                pthread_mutex_unlock(&hashr_mutex);                                                                                                                                                                    
                return((void *)0);                                                                                                                                                                                     
        }                                                                                                                                                                                                              
}                                                                                                                                                                                                                      
bool not_in_done(int* n,int * m)                                                                                                                                                                                       
{                                                                                                                                                                                                                      
        int i;                                                                                                                                                                                                         
        for(i=0;i<NHASH;i++)                                                                                                                                                                                           
        {                                                                                                                                                                                                              
                if(*m != n[i])                                                                                                                                                                                         
                        continue;                                                                                                                                                                                      
                else                                                                                                                                                                                                   
                        return 1;                                                                                                                                                                                      
        }                                                                                                                                                                                                              
        return 0;                                                                                                                                                                                                      
}                                                                                                                                                                                                                      

int main(void)                                                                                                                                                                                                         
{                                                                                                                                                                                                                      
        int err;                                                                                                                                                                                                       
        int num;                                                                                                                                                                                                       
        struct foo * fp;                                                                                                                                                                                               
        int done[3]={4,4,4};                                                                                                                                                                                           
        pthread_t tid;                                                                                                                                                                                                 
        char i=0;                                                                                                                                                                                                      
        for( num=0;num<NHASH;num++)                                                                                                                                                                                    
        {       
                //笔者这里创建线程的时候传递num*NHASH,旨在让新创建的节点都位于fh[0]所在的链表。                                                                                                                                                                                                      
                err = pthread_create(&tid, NULL, thr_fn,(void *)(num*NHASH));                                                                                                                                          
                if (err != 0)                                                                                                                                                                                          
                        errx(1, "can’t create thread:%d\n",num);                                                                                                                                                       
        }                                                                                                                                                                                                              
        for(;;)                                                                                                                                                                                                        
        {                                                                                                                                                                                                              
                for(num=0;num<NHASH;num++)                                                                                                                                                                             
                        if(not_in_done(done,&num) == 0)                                                                                                                                                                
                               if((fp = foo_find(num*NHASH)) != NULL)                                                                                                                                                 
                                {                                                                                                                                                                                      
                                        foo_printf(fp);                                                                                                                                                                
                                        done[i++]=num;                                                                                                                                                                 
                                        if(i>=NHASH)                                                                                                                                                                   
                                        {                                                                                                                                                                              
                                                pthread_mutex_lock(&hashr_mutex);                                                                                                                                      
                                                pthread_cond_broadcast(&hashr_cond);                                                                                                                                  
                                                pthread_mutex_unlock(&hashr_mutex);                                                                                                                                    
                                                exit(0);                                                                                                                                                               
                                        }                                                                                                                                                                              
                                }                                                                                                                                                                                      
        }                                                                                                                                                                                                              
}

看看执行结果:

[root@localhost ~]# ./11_4
thread 140605283890944, foo_id:6
thread 140605418784512, foo_id:6
thread 140605402101504, foo_id:3
thread 140605418784512, foo_id:3
thread 140605410494208, foo_id:0
thread 140605418784512, foo_id:0

  效果还不错,按照我们想象的执行了呢。140605418784512 为我们主thread的ID号,然后接着对比测试下:此时注释所有MutexCondition Variable,编译运行测试,结果如下:

测试一: 没有任何锁

[root@localhost ~]# ./11_4_1 
thread 140438852294400, foo_id:0
thread 140438835508992, foo_id:6
thread 140438860584704, foo_id:0
thread 140438843901696, foo_id:3
//这里程序并没有结束哦!

  接着第二次对比测试下,此时注释所有Mutex,保留Condition Variable。编译运行测试,结果如下:

测试二:保留Condition Variable

[root@localhost ~]# ./11_4_2     
thread 140276614813440, foo_id:0
thread 140276623103744, foo_id:0
thread 140276598028032, foo_id:6
thread 140276623103744, foo_id:6
thread 140276606420736, foo_id:3
thread 140276623103744, foo_id:3

  尝试去解释上面的结果:测试1中每个线程打印出了自己新加入的节点,这个在代码里面很容易解释,但是主thread却陷入死循环。此时因为每个子thread都free了自己新创建的节点,导致主thread找不到对应的节点,所以只能死循环。
  测试2中结果和完整版结果差不多,但是测试2中没有对哈希表采取任何保护措施!,这在本次的测试中并没有引起异常,因为本次实验代码只有foo_relefoo_alloc涉及对哈希表的更改 ,其他函数都是只读形式读取哈希表。可能发生一种竞态:每个子线程调用foo_alloc时,运行到如下所示代码时,可能会发生fh[idx] 前后不一致性。

//pthread_mutex_lock(&hashlock);
fp->f_next = fh[idx];
//这里被打断!
fh[idx] = fp;
//pthread_mutex_unlock(&hashlock);

实验二 :

题目:请构造一个使用RECURSIVE的Mutex的场合。

  笔者想实验一的代码的时候,就考虑能不能把实验二也囊括里面呢?所以想出来啦,修改如下所示:

[root@localhost ~]#  diff -Naur 11_4.c 11_5.c
--- 11_4.c      2018-02-09 17:48:17.463201599 +0800
+++ 11_5.c      2018-02-09 19:19:37.000000000 +0800
@@ -14,6 +14,7 @@

 struct foo *fh[NHASH];
 pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutexattr_t attr;
 pthread_mutex_t hashr_mutex = PTHREAD_MUTEX_INITIALIZER;
 pthread_cond_t hashr_cond = PTHREAD_COND_INITIALIZER;

@@ -56,29 +57,24 @@
 bool foo_rele(int id)
 {
         struct foo * fp;
-       struct foo * fp1;
+             struct foo * fp1;
         pthread_mutex_lock(&hashlock);
-        for (fp = fh[HASH(id)]; fp != NULL; fp = fp->f_next)
+        if((fp = foo_find(id)) == NULL)
         {
-                if (fp->f_id == id)
-                {
-                        if( fp == fh[HASH(id)])
-                        {
-                                fh[HASH(id)] = fp->f_next;
-                        }
-                        else
-                        {
-                            fp1=fh[HASH(id)];
-                                while (fp1->f_next != fp)
-                                        fp1 = fp1->f_next;
-                                fp1->f_next = fp->f_next;
-                        }
-                        break;
-                }
+                errx(1,"error in find a foo\n");
+        }
+        if( fp == fh[HASH(id)])
+        {
+                fh[HASH(id)] = fp->f_next;
+        }
+        else
+        {
+                fp1=fh[HASH(id)];
+                while (fp1->f_next != fp)
+                        fp1 = fp1->f_next;
+                fp1->f_next = fp->f_next;
         }
         pthread_mutex_unlock(&hashlock);
-        if(fp == NULL)
-                return 1;
         free(fp);
         return 0;
 }
@@ -124,8 +120,14 @@
         int done[3]={4,4,4};
         pthread_t tid;
         char i=0;
+        if ((err = pthread_mutexattr_init(&attr)) != 0)
+                errx(1,"pthread_mutexattr_init failed");
+        if ((err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) != 0)
+                errx(1, "can’t set recursive type");
+        if ((err = pthread_mutex_init(&hashlock, &attr)) != 0)
+                errx(1, "can’t create recursive mutex");
         for( num=0;num<NHASH;num++)
-        {                                                                                                                                                                                                             
+        {
                 err = pthread_create(&tid, NULL, thr_fn,(void *)(num*NHASH));
                 if (err != 0)
                         errx(1, "can’t create thread:%d\n",num);                                                                             

测试结果:

[root@localhost ~]# ./11_5
thread 139919336552192, foo_id:3
thread 139919353235200, foo_id:3
thread 139919344944896, foo_id:0
thread 139919353235200, foo_id:0
thread 139919328159488, foo_id:6
thread 139919353235200, foo_id:6

  分析一下吧,如果想在foo_rele中调用foo_find,那么必须采用PTHREAD_MUTEX_RECURSIVE 性质的Mutex,否则会产生死锁。其他就不解释啦,结果很正常呢。

写在最后:

  笔者自己想的题目,自己撸的代码,可能题目本身的场景就不合实际。本文的目的就是帮助理解下MutexPTHREAD_MUTEX_RECURSIVECondition Variable 。如果阅读完本文之后,您能有所收获,才是我继续写作的最大动力!顺便提一下,有时候竞态只是一条语句就可能产生,因为一条C语句可能对于的汇编语句是多条。有时候对于简单的操作的保护,选择Mutex不一定最适合。下一篇Spinlock也将基于该篇的场景进行论述。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LoveStackover/article/details/79300264
个人分类: UNIX C编程
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

多线程之Mutex和Condition Variable实验(三)

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭