免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 9339 | 回复: 45

[C] 谁能帮我解释这个无锁链表是怎么实现的,帮我分析一下 [复制链接]

论坛徽章:
0
发表于 2014-11-20 12:08 |显示全部楼层
本帖最后由 finyren 于 2014-11-20 12:09 编辑

  1. /*
  2. * Copyright (c) 2013, zylthinking
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without modification,
  6. * are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice,
  9. *    this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright notice,
  11. *    this list of conditions and the following disclaimer in the documentation and/or
  12. *    other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  16. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  17. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  18. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20. * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  21. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  22. * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */

  24. #ifndef lkf_h
  25. #define lkf_h

  26. struct lkf_node {
  27.     struct lkf_node* next;
  28. };

  29. struct lkf_list {
  30.     struct lkf_node root;
  31.     struct lkf_node** tail;
  32. };


  33. #define LKF_INIT(name) {.root = {NULL}, .tail = &(name.root.next)}
  34. #define LKF_LIST(name) \
  35. struct lkf_list name = LKF_INIT(name)

  36. #define INIT_LKF(name) \
  37. do { \
  38.     name->root.next = NULL; \
  39.     name->tail = &(name->root.next); \
  40. } while (0)

  41. static inline void lkf_node_put(struct lkf_list* list, struct lkf_node* node)
  42. {
  43.     struct lkf_node** ptr = __sync_lock_test_and_set(&(list->tail), &(node->next));
  44.     *ptr = node;
  45. }

  46. static inline struct lkf_node* lkf_node_get_one(struct lkf_list* list)
  47. {
  48.     struct lkf_node* head = __sync_lock_test_and_set(&(list->root.next), NULL);
  49.     if (head == NULL) {
  50.         return NULL;
  51.     }

  52.     struct lkf_node* next = head->next;
  53.     if (next != NULL) {
  54.         list->root.next = next;
  55.         head->next = head;
  56.         return head;
  57.     }

  58.     int b = __sync_bool_compare_and_swap(&(list->tail), &(head->next), &(list->root.next));
  59.     if (b) {
  60.         head->next = head;
  61.         return head;
  62.     }

  63.     list->root.next = head;
  64.     return NULL;
  65. }

  66. static inline struct lkf_node* lkf_node_get(struct lkf_list* list)
  67. {
  68.     struct lkf_node* ptr = __sync_lock_test_and_set(&(list->root.next), NULL);
  69.     if (ptr == NULL) {
  70.         return NULL;
  71.     }

  72.     struct lkf_node** last = __sync_lock_test_and_set(&(list->tail), &(list->root.next));
  73.     *last = ptr;
  74.     return *last;
  75. }

  76. static inline struct lkf_node* lkf_node_next(struct lkf_node* node)
  77. {
  78.     struct lkf_node* ptr = node->next;
  79.     if (ptr == NULL || ptr->next == NULL) {
  80.         return NULL;
  81.     }

  82.     if (ptr == node) {
  83.         return ptr;
  84.     }

  85.     node->next = ptr->next;
  86.     ptr->next = NULL;
  87.     return ptr;
  88. }

  89. #endif
复制代码
以下是测试代码

  1. #define _GNU_SOURCE
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <pthread.h>

  6. #include "lkf.h"
  7. static LKF_LIST(list);

  8. #define nrp 1000
  9. #define nrt 500

  10. static int run = 0;
  11. static struct lkf_node* pool = NULL;
  12. static int indx = 0;
  13. static int nr = 0;

  14. void* writer(void* any)
  15. {
  16.     while (run == 0) {
  17.         usleep(100);
  18.     }

  19.     for (int i = 0; i < nrp; ++i) {
  20.         int idx = __sync_fetch_and_add(&indx, 1);
  21.         struct lkf_node* nodep = &pool[idx];
  22.         nodep->next = NULL;
  23.         lkf_node_put(&list, nodep);
  24.     }

  25.     __sync_sub_and_fetch(&run, 1);
  26.     printf("writer exits\n");
  27.     return NULL;
  28. }

  29. void* reader(void* any)
  30. {
  31.     while (run == 0) {
  32.         usleep(100);
  33.     }

  34.     while (nr > 0) {
  35.         struct lkf_node* nodep = lkf_node_get(&list);
  36.         if (nodep == NULL) {
  37.             continue;
  38.         }

  39.         __sync_sub_and_fetch(&nr, 1);
  40.         struct lkf_node* cur = NULL;
  41.         do {
  42.             cur = lkf_node_next(nodep);
  43.             if (cur == nodep) {
  44.                 break;
  45.             }

  46.             if (cur != NULL) {
  47.                 __sync_sub_and_fetch(&nr, 1);
  48.             }
  49.         } while (1);
  50.     }

  51.     printf("reader exit\n");
  52.     return NULL;
  53. }

  54. void* reader2(void* any)
  55. {
  56.     while (run == 0) {
  57.         usleep(100);
  58.     }

  59.     while (nr > 0) {
  60.         struct lkf_node* nodep = lkf_node_get_one(&list);
  61.         if (nodep == NULL) {
  62.             continue;
  63.         }

  64.         __sync_sub_and_fetch(&nr, 1);
  65.     }
  66.     printf("reader2 exit\n");
  67.     return NULL;
  68. }

  69. int main(int argc, char** argv)
  70. {
  71.     int n = 0;
  72.     for (int i = 0; i < nrt; ++i) {
  73.         pthread_t tid;
  74.         if(0 == pthread_create(&tid, NULL, writer, NULL)) {
  75.             ++n;
  76.             pthread_detach(tid);
  77.         }
  78.     }

  79.     nr = nrp * n;
  80.     pool = (struct lkf_node *) malloc(sizeof(struct lkf_node) * nr);
  81.     if (pool == 0) {
  82.         printf("memory\n");
  83.         return 0;
  84.     }

  85.     int readers = 0;
  86.     for (int i = 0; i < 500; ++i) {
  87.         pthread_t tid;
  88.         if(0 == pthread_create(&tid, NULL, reader2, NULL)) {
  89.             ++readers;
  90.             pthread_detach(tid);
  91.         }
  92.     }

  93.     printf("run = %d\n", run);
  94.     printf("%d writer, %d reader, nr = %d\n", n, readers + 1, nr);
  95.     run = n;

  96.     time_t t1 = time(0);
  97.     if (nr > 0) {
  98.         //reader(0);
  99.     }

  100.     while (1) {
  101.         usleep(10);
  102.         struct lkf_node* nodep = 0;//lkf_node_get(&list);
  103.         time_t t2 = time(0);
  104.         if (t2 > t1 + 3) {
  105.             printf("%d %d %p\n", nr, indx, nodep);
  106.             t1 = t2;
  107.         }
  108.     }
  109.     return 0;
  110. }
复制代码

论坛徽章:
324
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
发表于 2014-11-20 13:42 |显示全部楼层

论坛徽章:
6
酉鸡
日期:2013-11-04 15:30:02巳蛇
日期:2014-01-23 10:36:23双鱼座
日期:2014-01-23 13:08:332015亚冠之鹿岛鹿角
日期:2015-09-03 14:36:002015亚冠之武里南联
日期:2015-09-18 10:48:1315-16赛季CBA联赛之山西
日期:2016-05-05 00:05:33
发表于 2014-11-20 13:49 |显示全部楼层
给你个简短的测试代码吧
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <stdlib.h>

  4. static int count = 0;


  5. void *test_func(void *arg)
  6. {
  7.         int i=0;
  8.         for(i=0;i<20000;++i){
  9.                 __sync_fetch_and_add(&count,1);
  10.         }
  11.         return NULL;
  12. }

  13. int main(int argc, const char *argv[])
  14. {
  15.         pthread_t id[20];
  16.         int i = 0;

  17.         for(i=0;i<20;++i){
  18.                 pthread_create(&id[i],NULL,test_func,NULL);
  19.         }

  20.         for(i=0;i<20;++i){
  21.                 pthread_join(id[i],NULL);
  22.         }

  23.         printf("%d\n",count);
  24.         return 0;
  25. }
复制代码

论坛徽章:
6
酉鸡
日期:2013-11-04 15:30:02巳蛇
日期:2014-01-23 10:36:23双鱼座
日期:2014-01-23 13:08:332015亚冠之鹿岛鹿角
日期:2015-09-03 14:36:002015亚冠之武里南联
日期:2015-09-18 10:48:1315-16赛季CBA联赛之山西
日期:2016-05-05 00:05:33
发表于 2014-11-20 13:49 |显示全部楼层

论坛徽章:
154
2022北京冬奥会纪念版徽章
日期:2015-08-07 17:10:5720周年集字徽章-年
日期:2022-10-26 16:44:2015-16赛季CBA联赛之深圳
日期:2022-11-02 14:02:4515-16赛季CBA联赛之八一
日期:2022-11-28 12:07:4820周年集字徽章-20	
日期:2023-07-19 08:49:4515-16赛季CBA联赛之八一
日期:2023-11-04 19:23:5115-16赛季CBA联赛之广夏
日期:2023-12-13 18:09:34
发表于 2014-11-20 14:16 |显示全部楼层
无锁链表安全吗??

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:56:11
发表于 2014-11-21 06:49 |显示全部楼层
据说,原子操作在大量并发的情况下对整个系统的运行效率影响非常大。不知是真是假。

论坛徽章:
12
巳蛇
日期:2013-09-16 15:32:242015年辞旧岁徽章
日期:2015-03-03 16:54:152015年亚洲杯之约旦
日期:2015-02-11 14:38:37双鱼座
日期:2015-01-05 11:05:47戌狗
日期:2014-12-08 09:41:18戌狗
日期:2014-08-15 09:29:29双子座
日期:2014-08-05 09:17:17卯兔
日期:2014-06-08 15:32:18巳蛇
日期:2014-01-27 08:47:08白羊座
日期:2013-11-28 21:04:15巨蟹座
日期:2013-11-13 21:58:012015年亚洲杯之科威特
日期:2015-04-17 16:51:51
发表于 2014-11-21 09:33 |显示全部楼层
cobras 发表于 2014-11-21 06:49
据说,原子操作在大量并发的情况下对整个系统的运行效率影响非常大。不知是真是假。


原子操作就是实现锁的基本工具。楼主贴的代码,实际是实现了自旋锁。我见过几乎所有的所谓“无锁”数据结构,都不过
是自己实现了自旋锁。

自旋锁在无法取得锁的时候会忙等,冲突几率大的时候白白消耗CPU。

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
发表于 2014-11-21 12:23 |显示全部楼层
zhaohongjian000 发表于 2014-11-21 09:33
原子操作就是实现锁的基本工具。楼主贴的代码,实际是实现了自旋锁。我见过几乎所有的所谓“无锁”数据 ...


我可没有自旋啊, 我只不过返回空, 你愿不愿意自旋, 就看你愿不愿意接着再调用一次了; 和我有什么关系, 你自己去干其他的我也没有拦着你啊
这玩意最大的毛病在于读 lkf_get_one 的时候实际是单线程的, 而且, 似乎可以实现的更好些, 但当时脑子一根筋了

论坛徽章:
12
巳蛇
日期:2013-09-16 15:32:242015年辞旧岁徽章
日期:2015-03-03 16:54:152015年亚洲杯之约旦
日期:2015-02-11 14:38:37双鱼座
日期:2015-01-05 11:05:47戌狗
日期:2014-12-08 09:41:18戌狗
日期:2014-08-15 09:29:29双子座
日期:2014-08-05 09:17:17卯兔
日期:2014-06-08 15:32:18巳蛇
日期:2014-01-27 08:47:08白羊座
日期:2013-11-28 21:04:15巨蟹座
日期:2013-11-13 21:58:012015年亚洲杯之科威特
日期:2015-04-17 16:51:51
发表于 2014-11-21 15:04 |显示全部楼层
zylthinking 发表于 2014-11-21 12:23
我可没有自旋啊, 我只不过返回空, 你愿不愿意自旋, 就看你愿不愿意接着再调用一次了; 和我有什么关 ...


自旋锁也可以trylock呀,比如pthread_spin_trylock。当然,trylock就不是忙等了。

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
发表于 2014-11-21 17:01 |显示全部楼层
本帖最后由 zylthinking 于 2014-11-21 17:08 编辑
zhaohongjian000 发表于 2014-11-21 15:04
自旋锁也可以trylock呀,比如pthread_spin_trylock。当然,trylock就不是忙等了。


那你干脆说所有的无锁结构不过都实现了锁得了, 反正就算是 mutex, 还有 pthread_mutex_try_lock 呢;
可以用 spin_lock  实现 读、写 不互斥?  
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP