关于double-check 和barrier

参考:http://blog.sina.com.cn/s/blog_597a437101011o66.html 最后的barrier部分自己补充的。

多线程问题也常常和一种lazy-initialize的设计模式联系在一起。在这里就会慢慢引出double-check。lazy-initialize讲的是,对于一些特别复杂的对象,让程序在第一次调用它的时候再对它进行初始化,而且保证仅仅初始化一次。
首先想到的设计是这样的:
Class A
{
private ComplexClass _result = null;
public ComplexClass GetResult()
{
if(_result == null)
{
_result = new ComplexClass();
}
return _result;
}
}
但是这样有一个问题。ComplexClass的构造过程较长的话,当第一个线程还在进行ComplexClass构造的时候,_result可能是null,也可能指向了一个尚未初始化完成的对象。这样,要么两个线程初始化了两次ComplexClass,要么第二个线程会返回一个指向不完整对象的引用。所以,在这里需要用到一个锁,如下所示:
ComplexClass GetResult()
{
lock(_lock)
{
if(_result == null)
{
_result = new ComplexClass();
}
}
return _result;
}
这样,虽然多线程的问题解决了,但是每一次需要使用result时都会请求锁,而请求锁对程序的性能是有很大影响的,因此我们在lock的外面再加一层check:
ComplexClass GetResult()
{
if(_result == null)
{
lock(_lock)
{
if(_result == null)
{
_result = new ComplexClass();
}
}
}
return _result;
}
这样,对于所有初始化完成后的请求,就都不用请求锁,而是直接返回_result。也就是说两个if的作用是为了让锁的请求调用开销减小。
但是还是存在一点问题。对于一些编程语言来说,_result = new ComplexClass();这句代码是三个步骤:1:分配动态内存,2:在内存上进行构造函数的调用,3:返回内存地址给指针_result。但是步骤2和步骤3有时候顺序会颠倒。这会使得_result指向一个仅有内存但是还没有完全初始化的对象。如果此时,线程B并发访问,那么线程B会判断_result已经不是null了,而这时其实初始化尚未完成,这时线程B就直接返回了一个部分初始化的对象,会造成程序的崩溃。那么,这个问题怎么解决呢?一般的解决方法是在程序内部再加一个局部变量(标识变量)做一层缓冲:
ComplexClass GetResult()
{
ComplexClass result;
if(_result == null)
{
lock(_lock)
{
if(_result == null)
{
result = new ComplexClass();
_result = result;
}
}
}
return _result;
}
当然,CPU提供的barrier指令也有上述功能,该指令的作用是让barrier前后的指令不会互相越界,就是barrier前面的指令不会跑到后面执行,反之亦然。所以上面的代码也可以再加上一个barrier()。
ComplexClass GetResult()
{
if(_result == null)
{
lock(_lock)
{
if(_result == null)
{
ComplexClass result = new ComplexClass();
barrier();
_result = result;
}
}
}
return _result;
}

这样,上面的问题就彻底解决了~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在CUDA编程中,m_barrier和named barrier是两种常用的线程同步机制,它们可以有效地协调线程之间的合作和同步,提高程序的并行性和效率。 1. m_barrier m_barrier是一种基于硬件实现的线程同步机制,它可以让所有线程在同一时刻停止执行,直到所有线程都到达barrier点。m_barrier的使用非常简单,只需要在需要同步的地方调用cudaDeviceSynchronize()函数即可: ``` cudaDeviceSynchronize(); ``` 这样,所有线程都会在该语句处停止执行,直到所有线程都执行完该语句后,才会继续执行下一条语句。 需要注意的是,m_barrier的性能可能受到线程数的影响,因为它需要等待所有线程都到达barrier点才能继续执行。 2. named barrier named barrier是一种基于软件实现的线程同步机制,它可以让不同线程块之间进行同步,提高程序的并行性和效率。named barrier需要先创建一个barrier对象,然后在需要同步的地方调用barrier.sync()函数进行同步。 例如,可以使用以下代码创建一个named barrier对象: ``` cuda::barrier<cuda::thread_scope_block> my_barrier(blockDim.x); ``` 其中,cuda::thread_scope_block表示线程块作用域,blockDim.x表示线程块的大小。 然后,可以在需要同步的地方调用my_barrier.sync()函数进行同步: ``` my_barrier.sync(); ``` 需要注意的是,named barrier的性能可能受到线程块数的影响,因为它需要等待所有线程块都到达barrier点才能继续执行。 总的来说,m_barrier和named barrier都是重要的线程同步机制,在CUDA编程中应用广泛。需要根据具体情况选择合适的同步机制,并进行优化以提高程序的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值