以下 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# 内存模型如何处理此问题?

伙计们,谢谢大家的回答。非常感谢。

以下 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");
}

永远不要将代码放在不需要在锁内的锁内。这是由于这造成的延迟。如果您有很多线程,则可以通过消除所有这些不必要的等待来获得很多性能。

希望对:)有所帮助