Dan*_*ani 22 c# multithreading locking
我在C#中有一个函数,可以从多个线程多次调用,我希望它只能完成一次,所以我想到了这个:
class MyClass
{
bool done = false;
public void DoSomething()
{
lock(this)
if(!done)
{
done = true;
_DoSomething();
}
}
}
Run Code Online (Sandbox Code Playgroud)
问题是_DoSomething需要很长时间,我不希望很多线程等待它,只要他们可以看到这done是真的.
这样的事情可以解决方法:
class MyClass
{
bool done = false;
public void DoSomething()
{
bool doIt = false;
lock(this)
if(!done)
doIt = done = true;
if(doIt)
_DoSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
但只需手动锁定和解锁就会好得多.
我怎样才能手动锁定和解锁lock(object)?我需要它使用相同的接口,lock因此这种手动方式lock将相互阻塞(对于更复杂的情况).
PVi*_*itt 30
该lock关键字仅仅是Monitor.Enter和Monitor.Exit语法糖:
Monitor.Enter(o);
try
{
//put your code here
}
finally
{
Monitor.Exit(o);
}
Run Code Online (Sandbox Code Playgroud)
是相同的
lock(o)
{
//put your code here
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 28
托马斯建议双重检查锁定他的答案.这是有问题的.首先,你不应该使用低锁技术,除非你已经证明你有一个真正的性能问题,这是通过低锁技术解决的.低锁定技术很难做到正确.
其次,这是有问题的,因为我们不知道"_DoSomething"会做什么,或者我们将依赖它的行为的后果.
第三,正如我在上面的评论中指出的那样,当另一个线程实际上仍处于执行过程中时,返回_DoSomething已"完成"似乎很疯狂.我不明白为什么你有这个要求,我会认为这是一个错误.即使我们在"_DoSomething"之后设置"完成",这种模式的问题仍然存在.
考虑以下:
class MyClass
{
readonly object locker = new object();
bool done = false;
public void DoSomething()
{
if (!done)
{
lock(locker)
{
if(!done)
{
ReallyDoSomething();
done = true;
}
}
}
}
int x;
void ReallyDoSomething()
{
x = 123;
}
void DoIt()
{
DoSomething();
int y = x;
Debug.Assert(y == 123); // Can this fire?
}
Run Code Online (Sandbox Code Playgroud)
这在C#的所有可能实现中都是线程安全的吗? 我认为不是.请记住,处理器缓存可能会及时移动非易失性读取.C#语言保证挥发性的读取,但始终对于像锁临界执行点预订的,它保证了非易失性读取都执行一个线程中保持一致,但它并不能保证非易失性读取在任何一致跨越执行线程的方式.
我们来看一个例子.
假设有两个线程,Alpha和Bravo.两者都在MyClass的新实例上调用DoIt.怎么了?
在线程Bravo上,处理器高速缓存碰巧执行(非易失性!)获取x的内存位置,其中包含零."完成"恰好位于不同的内存页面上,而目前尚未将其提取到缓存中.
在线程Alpha在"同一时间"在不同的处理器上DoIt调用DoSomething.线程Alpha现在运行在那里的一切.当线程Alpha完成其工作时,完成为真,并且Alpha在Alpha处理器上为123.Thread Alpha的处理器将这些事实刷回主存储器.
Thread bravo现在运行DoSomething.它将包含"done"的主内存页面读入处理器缓存,并看到它是真的.
所以现在"完成"是正确的,但"x"在线程Bravo的处理器缓存中仍为零.Thread Bravo不需要使包含"x"的缓存部分无效,因为在线程Bravo上,"完成"和"x"的读取都不是易失性读取.
建议的双重检查锁定版本实际上并不是双重检查锁定.当您更改双重检查的锁定模式时,您需要从头开始重新开始并重新分析所有内容.
使这个版本的模式正确的方法是至少将"完成"的第一次读取变为易失性读取.然后,不允许读取"x"将易失性读取"提前"移动到"完成".
| 归档时间: |
|
| 查看次数: |
32343 次 |
| 最近记录: |