以下 C# 代码线程是否安全
本文关键字:是否 安全 线程 代码 以下 | 更新日期: 2023-09-27 17:56:30
我正在尝试学习 C# 中的线程。今天我在 http://www.albahari.com/threading/播种以下代码:
class ThreadTest
{
bool done;
static void Main()
{
ThreadTest tt = new ThreadTest(); // Create a common instance
new Thread (tt.Go).Start();
tt.Go();
}
// Note that Go is now an instance method
void Go()
{
if (!done) { done = true; Console.WriteLine ("Done"); }
}
}
在Java中,除非将"done"定义为易失性,否则代码将不安全。C# 内存模型如何处理此问题?
伙计们,谢谢大家的回答。非常感谢。
嗯,有一个明确的竞争条件,他们都可以将done
视为假并执行if
体 - 无论内存模型如何,都是如此。使done
易变不会解决这个问题,在Java中也不会解决这个问题。
的,在一个线程中所做的更改可能会发生,但在另一个线程中之前不可见是可行的。这取决于 CPU 架构等。作为我的意思的一个例子,请考虑此程序:
using System;
using System.Threading;
class Test
{
private bool stop = false;
static void Main()
{
new Test().Start();
}
void Start()
{
new Thread(ThreadJob).Start();
Thread.Sleep(500);
stop = true;
}
void ThreadJob()
{
int x = 0;
while (!stop)
{
x++;
}
Console.WriteLine("Counted to {0}", x);
}
}
虽然在我目前的笔记本电脑上这确实会终止,但我使用过其他机器,其中几乎完全相同的代码将永远运行 - 它永远不会"看到"在第二个线程中stop
的变化。
基本上,我尽量避免编写无锁代码,除非它使用真正了解他们的东西的人提供的更高级别的抽象 - 例如 .NET 4 中的并行扩展。
不过,有一种方法可以使此代码无锁定并轻松更正,使用 Interlocked
.例如:
class ThreadTest
{
int done;
static void Main()
{
ThreadTest tt = new ThreadTest(); // Create a common instance
new Thread (tt.Go).Start();
tt.Go();
}
// Note that Go is now an instance method
void Go()
{
if (Interlocked.CompareExchange(ref done, 1, 0) == 0)
{
Console.WriteLine("Done");
}
}
}
在这里,值的更改和测试作为一个单元执行:CompareExchange
如果当前值为 0,则只会将值设置为 1,并将返回旧值。因此,只有一个线程会看到返回值 0。
要记住的另一件事:您的问题相当模棱两可,因为您尚未定义"线程安全"的含义。我已经猜到了你的意图,但你从来没有说清楚。阅读Eric Lippert的这篇博文 - 这是非常值得的。
不,它不是线程安全的。您可能让一个线程检查条件 ( if(!done)
),另一个线程检查相同的条件,然后第一个线程执行代码块中的第一行 ( done = true
)。
您可以使用锁使其线程安全:
lock(this)
{
if(!done)
{
done = true;
Console.WriteLine("Done");
}
}
即使在有volatile
的Java中,两个线程都可以通过WriteLine
进入块。
如果要互斥,则需要使用真正的同步对象,例如锁。
使用原子比较并在 if 测试中设置时,这是线程安全的
if(atomicBool.compareAndSet(false,true)){
Console.WriteLine("Done");
}
你应该做这样的事情:
class ThreadTest{
Object myLock = new Object();
...
void Go(){
lock(myLock){
if(!done)
{
done = true;
Console.WriteLine("Done");
}
}
}
您想要使用通用对象而不是"this"的原因是,如果您的对象(也称为"this")发生了变化,则将其视为另一个对象。因此,您的锁不再起作用。
您可能会考虑的另一件小事是这个。这是一个"良好做法"的事情,所以没有什么严重的。
class ThreadTest{
Object myLock = new Object();
...
void Go(){
lock(myLock){
if(!done)
{
done = true;
}
}
//This line of code does not belong inside the lock.
Console.WriteLine("Done");
}
永远不要将代码放在不需要在锁内的锁内。这是由于这造成的延迟。如果您有很多线程,则可以通过消除所有这些不必要的等待来获得很多性能。
希望对:)有所帮助