zoukankan      html  css  js  c++  java
  • LockFree的栈实现及与加锁实现的性能对比

      在编写多线程程序时,临界资源的处理常常需要互斥量、读写锁等来加以保护。这时需要考虑锁的粒度问题,粒度太粗,会出现很多线程阻塞等待相同的锁,源自并发性的改善微乎其微;如果锁的粒度太细,那么过多的锁开销会使系统性能受到影响,而且代码变得相当复杂。除此之外,还要细致的考虑各种dead lock问题。

      因此,对于某些关键数据结构(临界资源),可以考虑使用Lock Free的实现手段。一个Lock Free的程序能够确保执行它的所有线程至少有一个能够继续往下执行,从而免疫了死锁等问题。Lock Free算法需要对应的原子操作加以支持,比如CAS(compare-and-swap)及其变种。CAS实现的逻辑如下:

    bool CAS(inptr_t* addr, intptr_t oldv, intptr_t newv)
    atomically{
    if(*addr == oldv)
    {
    *addr = newv;
    return true;
    }
    else
      {
    return false
    }

    }

          接下来给出有锁和无锁的栈实现,以及最后的性能测试数据。

          有锁实现的栈:

          lock_stack.h

     1 #ifndef __LOCK_STACK_H
    2 #define __LOCK_STACK_H
    3 #include<pthread.h>
    4
    5 typedef int value_t;
    6
    7 struct cell
    8 {
    9 struct cell *next;
    10 value_t value;
    11 };
    12
    13 struct stack
    14 {
    15 struct cell *top;
    16 pthread_mutex_t lock;
    17 };
    18
    19 struct stack *stack_alloc(void);
    20 void stack_push(struct stack *fp,struct cell *cl);
    21 struct cell *stack_pop(struct stack *fp);
    22
    23 #endif

      lock_stack.c

     1 #include "lock_stack.h"
    2 #include <pthread.h>
    3 #include <stdlib.h>
    4
    5 struct stack *stack_alloc(void)
    6 {
    7 struct stack *fp;
    8 if((fp = (struct stack*)malloc(sizeof(struct stack))) != NULL)
    9 {
    10 fp->top = NULL;
    11 if(pthread_mutex_init(&fp->lock,NULL) != 0)
    12 {
    13 free(fp);
    14 return NULL;
    15 }
    16 }
    17 return fp;
    18 }
    19
    20 void stack_push(struct stack *fp,struct cell *cl)
    21 {
    22 pthread_mutex_lock(&fp->lock);
    23
    24 cl->next = fp->top;
    25 fp->top = cl;
    26
    27 pthread_mutex_unlock(&fp->lock);
    28 }
    29
    30 struct cell *stack_pop(struct stack *fp)
    31 {
    32 cell *head;
    33 pthread_mutex_lock(&fp->lock);
    34
    35 head = fp->top;
    36 if(head == NULL)
    37 {
    38 pthread_mutex_unlock(&fp->lock);
    39 return head;
    40 }
    41
    42 fp->top = head->next;
    43 pthread_mutex_unlock(&fp->lock);
    44 }

      无锁实现的栈:

      lock_free_stack.h

     1 #ifndef __LOCK_FREE_STACK_H
    2 #define __LOCK_FREE_STACK_H
    3 #include<pthread.h>
    4
    5 typedef int value_t;
    6
    7 struct cell
    8 {
    9 struct cell *next;
    10 value_t value;
    11 };
    12
    13 struct stack
    14 {
    15 struct cell *top;
    16 };
    17
    18 struct stack *stack_alloc(void);
    19 void stack_push(struct stack *fp,struct cell *cl);
    20 struct cell *stack_pop(struct stack *fp);
    21
    22 #endif

      lock_free_stack.c

     1 #include "lock_free_stack.h"
    2 #include <pthread.h>
    3 #include <stdlib.h>
    4
    5 struct stack *stack_alloc(void)
    6 {
    7 struct stack *fp;
    8 if((fp = (struct stack*)malloc(sizeof(struct stack))) != NULL)
    9 {
    10 fp->top = NULL;
    11 }
    12 return fp;
    13 }
    14
    15 void stack_push(struct stack *fp,struct cell *cl)
    16 {
    17 do{
    18 cl->next = fp->top;
    19 }while(!__sync_bool_compare_and_swap(&fp->top,cl->next,cl));
    20 }
    21
    22 struct cell *stack_pop(struct stack *fp)
    23 {
    24 cell *head,*next;
    25
    26 do
    27 {
    28 head = fp->top;
    29 if(head == NULL)
    30 {
    31 break;
    32 }
    33 next = head->next;
    34 }while(!__sync_bool_compare_and_swap(&fp->top,head,next));
    35 return head;
    36 }

      主程序使用100个线程,每个线程对同一栈进行500000次随机的push/pop操作(具体进行某个操作rand确定),并记录了每个线程的执行时间,代码如下:

     1 #ifdef __LOCK_VERSION
    2 #include "lock_stack.h"
    3 #endif
    4 #ifdef __LOCK_FREE_VERSION
    5 #include "lock_free_stack.h"
    6 #endif
    7 #include <stdlib.h>
    8 #include <stdio.h>
    9 #include <unistd.h>
    10 #include <time.h>
    11
    12 void* thr_fn(void *fp)
    13 {
    14 time_t t1 = time(NULL);
    15 for(int i = 0; i != 500000; i++)
    16 {
    17 int r = rand()%2;
    18 struct cell *cl;
    19
    20   if(r == 0)
    21   {
    22   if((cl = (struct cell*)malloc(sizeof(struct cell))) != NULL)
    23   {
    24     stack_push((struct stack*)fp,cl);
    25   }
    26   printf("push!\n");
    27   }
    28   else
    29   {
    30    cl = stack_pop((struct stack*)fp);
    31   if(cl != NULL)
    32   {
    33 //free(cl);
    34   }
    35   printf("pop!\n");
    36 }
    37 }
    38
    39 printf("cost time: %d\n",time(NULL)-t1);
    40
    41 return NULL;
    42 }
    43
    44 int main()
    45 {
    46 struct stack *fp;
    47 fp = stack_alloc();
    48
    49 pthread_t tid;
    50 for(int i = 0; i < 100; i++)
    51 {
    52 pthread_create(&tid,NULL,thr_fn,(void *)fp);
    53 }
    54
    55 sleep(1000);
    56
    57 return 0;
    58 }

      makefile文件如下:

    1 lock_free:
    2 g++ lock_free_stack.c main.c -lpthread -D__LOCK_FREE_VERSION -march=nocona -o lock_free
    3 lock:
    4 g++ lock_stack.c main.c -lpthread -D__LOCK_VERSION -o lock
    5
    6 clean:
    7 rm lock lock_free

      得到最终的测试数据如下所示:

      相比较而言,在性能上lock_free_stack有微弱的优势。

    注:

    1)大家在看代码时可能已经注意到了,main.c 33行free()函数被注释掉了,这是有意为之,在主动进行内存管理时,Lock Free结构会遇到ABA问题,需要CAS2配合Sequence Number加以解决。感兴趣的读者可以继续阅读文后的参考文献;

    2)主线程需要等待所有的子线程执行完毕,这里不好使用pthread_join,本文作者采用取巧的办法,调用sleep函数进行等待,具体等待时间可能和测试环境相关;

    3)__sync_bool_compare_and_swap 是由GCC提供的内置函数(GCC 4.1或更高版本),编译时需要指定-march参数;

    参考资料:

    [1]搜狗实验室   C10K与高性能程序续篇  http://www.sogou.com/labs/report/4-2.pdf

    [2]W.Richard Stevens     Advanced Programming in the Unix Environment  11.6节 线程同步

    [3]设计不适用互斥锁的并发数据结构  http://www.ibm.com/developerworks/cn/aix/library/au-multithreaded_structures2/

    [4]解决了“undefined reference to `__sync_bool_compare_and_swap_4”的编译错误   http://raylinn.iteye.com/blog/347323

    [5]compare_and_swap  http://en.wikipedia.org/wiki/Compare-and-swap

  • 相关阅读:
    HDU 4619 Warm up 2 (多校)
    深入浅出Node.js (9)
    HDU 1106 排序(排序)
    字符串相关心得
    HDU 2547 无剑无我(数学)
    HDU 2549 壮志难酬(字符串,处理小数点)
    HDU 2551 竹青遍野(循环,水)
    HDU 2552 三足鼎立(数学函数)
    HDU 2555 陷阱(模拟,结构体数组)
    HDU 2561 第二小整数(排序,水)
  • 原文地址:https://www.cnblogs.com/liuhao/p/2295671.html
Copyright © 2011-2022 走看看