免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
1234下一页
最近访问板块 发新帖
查看: 14777 | 回复: 39
打印 上一主题 下一主题

[算法] 什么情况下必须使用非递归锁? [复制链接]

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:49:45
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-04-08 13:32 |只看该作者 |倒序浏览
刚才认真想了一下,理论上讲什么情况下必须使用非递归锁?有没有不使用非递归锁就没法解决的多线程问题?俺是没找到,前面那个帖子里讨论的问题用递归锁、非递归锁都能实现。{:3_195:}

论坛徽章:
0
2 [报告]
发表于 2011-04-08 13:38 |只看该作者
这个确实没有。

说道递归锁影响效率,其实危言耸听。
在已经锁定的情况下,再锁一次递归锁,一般只是加一个计数而已。

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:49:45
3 [报告]
发表于 2011-04-08 13:51 |只看该作者
回复 2# AD8018


    那为啥pthread支持非递归锁?只支持一个递归锁不就完了。

论坛徽章:
0
4 [报告]
发表于 2011-04-08 13:53 |只看该作者
回复 3# koolcoy

嗯,win32的锁就是这么做的啊
    pthread这个俺非常不理解

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
5 [报告]
发表于 2011-04-08 14:00 |只看该作者
理论上使用memcpy的地方, 都可以使用memmove。
理论上所有内存管理库都该扔掉, 因为总是可以使用 malloc/free/calloc/realloc。
理论上锁都不该划分这么细, 什么critical section, 什么mutex, 什么spin, 什么condition, 都可以用信号量搞定。

这究竟是为什么呢

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
6 [报告]
发表于 2011-04-08 14:10 |只看该作者
回复 2# AD8018

这应该不是危言耸听。
>> 在已经锁定的情况下,再锁一次递归锁,一般只是加一个计数而已。
如何判断已经锁定?
简单的if就行么? 还是需要调用interlockXXX函数?
据说后者是会锁定总线的。

具体效率对比我也没有实际数据。
还是以linux为例子吧。
递归锁是否会影响效率, 是看你是否关心这点效率
对不关心的场景, 那就是危言耸听; 对关心的场景, 那就是不得不为之。

论坛徽章:
0
7 [报告]
发表于 2011-04-08 14:48 |只看该作者
回复 6# OwnWaterloo

这样有问题吗?
用非递归的mutex
void init(mutex *m);
void lock(mutex *m);
void unlock(mutex *m);

包出对应的三个递归锁的mutex_r的函数。

类似代码俺用了很久了,被你这么一说,回过头来想想。轻点砸。。

  1. struct mutex_r
  2. {
  3.         int count;
  4.         int thread_id;
  5.         mutex mutex;
  6. };

  7. void init_r(mutex_r *m)
  8. {
  9.         m->count = 0;
  10.         init(&m->mutex);
  11. }

  12. void lock_r(mutex_r *m)
  13. {
  14.         if(m->thread_id == get_current_thread_id() && m->count > 0)
  15.                 ++m->count;
  16.         else
  17.         {
  18.                 lock(&m->mutex);
  19.                 m->thread_id = get_current_thread_id();
  20.                 m->count = 1;
  21.         }
  22. }

  23. void unlock_r(mutex_r *m)
  24. {
  25.         assert(m->count > 0);

  26.         --m->count;
  27.         if(m->count == 0)
  28.                 unlock(&m->mutex);
  29. }
复制代码

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
8 [报告]
发表于 2011-04-08 17:24 |只看该作者
回复 7# AD8018

没想出什么问题……
可能是我记错了……  我再想想……


去翻了翻glibc的代码, 对owner的测试是普通的if。

  1.   else if (__builtin_expect (type == PTHREAD_MUTEX_RECURSIVE_NP, 1))
  2.     {
  3.       /* Recursive mutex.  */

  4.       /* Check whether we already hold the mutex.  */
  5.       if (mutex->__data.__owner == id)
  6.         {
  7.           /* Just bump the counter.  */
  8.           if (__builtin_expect (mutex->__data.__count + 1 == 0, 0))
  9.             /* Overflow of the counter.  */
  10.             return EAGAIN;

  11.           ++mutex->__data.__count;

  12.           return 0;
  13.         }

  14.       /* We have to get the mutex.  */
  15.       LLL_MUTEX_LOCK (mutex);

  16.       assert (mutex->__data.__owner == 0);
  17.       mutex->__data.__count = 1;
  18.     }
复制代码
而pthread-w32 是先用互锁测试, 然后在判断是否是owner

  1.       pthread_t self = pthread_self();

  2.       if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE(
  3.                    (PTW32_INTERLOCKED_LPLONG) &mx->lock_idx,
  4.                    (PTW32_INTERLOCKED_LONG) 1,
  5.                    (PTW32_INTERLOCKED_LONG) 0) == 0)
  6.         {
  7.           mx->recursive_count = 1;
  8.           mx->ownerThread = self;
  9.         }
  10.       else
  11.         {
  12.           if (pthread_equal (mx->ownerThread, self))
  13.             {
  14.               if (mx->kind == PTHREAD_MUTEX_RECURSIVE)
  15.                 {
  16.                   mx->recursive_count++;
  17.                 }
  18.               else
  19.                 {
  20.                   result = EDEADLK;
  21.                 }
  22.             }
复制代码

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
9 [报告]
发表于 2011-04-08 22:20 |只看该作者
回复 7# AD8018

thread_id有问题。  且不说没有初始化是垃圾值。

thread1:
lock_r(&m);
unlock_r(&m);

此时 thread_id=thread1

thread1:
lock_r(&m);
  if 测试成功
  切换

thread2:
lock_r(&m);
  if 测试失败
  lock成功, 此时被thread2持有。

thread1:
  继续执行++count。 且返回

此时 thread1 与 thread2 都没有被block。

------ ------ ------ reset owner ------ ------ -------

无论 owner 一开始被设置为何值。
在unlock, count==0 时被设置为何值。

只要这个值会和某个thread_id重复, 那还是有上述问题。
lock中if (owner==thread_id()) 成功, 但其实该线程并没有持有锁。

------ ------ ------ invalid thread id ------ ------ -------

绝对不会出现的thread_id。

Windows上是有的, 0不会被作为线程号。

非Windows上我就不了解了。
pthread_t 甚至不一定是整数:

  1. IEEE Std 1003.1-2001/Cor 2-2004, item XBD/TC2/D6/26 is applied, adding pthread_t to the list of types that are not required to be arithmetic types, thus allowing pthread_t to be defined as a structure.
复制代码
先姑且假设这个号存在, thread_id() 返回一定不会和它相同。

无论是 if (owner==thread_id()) // owned by this thread, ++count
还是 if (owner==invalid_id) // available, owner=thread_id
测试与后面的操作都不是原子的。
都可能会出现测试成功, 然后被切换, 使得另一个线程也测试成功。

这里需要一个原子操作:

  1. int compare_and_swap(int* dst, int test, int set)
  2. {
  3.     int old = *dst;
  4.     if (old == test) *dst = set;
  5.     return old;
  6. }
复制代码
那么就可以:
id = thread_id();
x = CAS(&owner, invalid_id, id);
if (x==invalid_id) // available -> owned by this thread
...
else if (x==id) // already owned by this thread
...
else // owned by other thread
...


再回到非Windows。
CAS以及其他 atomic 也没有被posix定义。
即使定义了, pthread_t 也不一定是整数。
而且是属于改动后对实现放宽了限制

这和改动后加强了对实现的限制不同。
比如C99 , 新增加了所有的 struct(union)的指针都有相同的对齐与大小, 这是C89中没有的。
也就是说, 即使在C89中也可以假设 struct 的指针有相同的大小与对齐(那些不满足的实现可能已经被历史淘汰了)。

改动后对实现放宽了限制, 可能确实有实现不满足原有的限制 —— 这只是我的猜测了。

论坛徽章:
0
10 [报告]
发表于 2011-04-08 22:58 |只看该作者
if(m->thread_id == get_current_thread_id() && m->count > 0)

测试成功,必须满足 count > 0

要count > 0, 一定在某个lock执行过这两句之后:
    m->thread_id = get_current_thread_id();
    m->count = 1;

所以没有问题啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP