Comparing Threads for equality

2019-07-22 12:58发布

I am looking for a way to explain that it's unreasonable to sprinkle high-level business logic with calls to ReferenceEquals().

Here's a code snippet that I have a problem with (precondition in a method, designed to throw if we're on a wrong thread):

if (!object.ReferenceEquals(Thread.CurrentThread, RequestHandlerThread))

Is it reliable to write this instead:

if (Thread.CurrentThread != RequestHandlerThread)

I suggested to use ManagedThreadIds in the comparison based on what I commonly see in tutorials. Adversary says that comparison for reference equality seems more object-oriented.

Here's (roughly) what I saw in Reflector's view of System.Object with .NET 4.0. Keep in mind that Thread class is sealed, and has no overload for operator==.

public static bool ReferenceEquals(object objA, object objB)
{
    return (objA == objB);
}

public static bool Equals(object objA, object objB)
{
    return (objA == objB || 
        (objA != null && objB != null && objA.Equals(objB)));
}

Here are some basic tests, verifying operation on thread pool... Did I miss any significant tests?

using System.Threading;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ConsoleApplicationX
{
   class Program
   {
      static readonly Thread mainThread;

      static Program()
      {
         mainThread = Thread.CurrentThread;
      }

      static void Main(string[] args)
      {
         Thread thread = Thread.CurrentThread;
         if (thread != Thread.CurrentThread)
            Debug.Fail("");

         if(Thread.CurrentThread != thread)
            Debug.Fail("");

         if (thread != mainThread)
            Debug.Fail("");

         var task = Task.Factory.StartNew(() => RunOnBackground(thread));
         task.Wait();

         var anotherThread = new Thread(new ParameterizedThreadStart(RunInAnotherThread));
         anotherThread.Start(thread);
      }

      static void RunOnBackground(Thread fromInitial)
      {
         if (Thread.CurrentThread == fromInitial)
            Debug.Fail("");

         if (fromInitial != mainThread)
            Debug.Fail("");
      }

      static void RunInAnotherThread(object fromInitialAsObject)
      {
         var fromInitial = (Thread)fromInitialAsObject;

         if (Thread.CurrentThread == fromInitial)
            Debug.Fail("");

         if (fromInitial != mainThread)
            Debug.Fail("");
      }
   }
}

4条回答
时光不老,我们不散
2楼-- · 2019-07-22 13:35

Trying not to preach, but ... In general, if there is an identity property to use, in this case

Thread.ManagedThreadId

it should be used for equality comparisons unless there is a performance constraint for the executing code. The semantics of a pointer comparison are a lot different than equality. Also, the pointer comparison is dependent upon the implementation of Thread in mscorlib.

查看更多
欢心
3楼-- · 2019-07-22 13:37

You are correct. As your reflection inquiry shows, the two ways are equivalent (barring an extra method call in the adversary's way)

时光不老,我们不散
4楼-- · 2019-07-22 13:43

Short answer:

Use ManagedThreadId property for comparisons.

Simple Example:

Imagine we have a class called StackOverflow.MessageThread. Programmer fat-fingers a precondition for a function, saying Debug.Assert(Thread.CurrentThread == messageThread). Precondition fails at runtime. If the dev would reach for ManagedThreadId, he'd find out at edit-time that it's impossible, and would fix the problem earlier in the development cycle.

查看更多
趁早两清
5楼-- · 2019-07-22 13:51

I could see how an implementation could use two references that are different, but would still point to the same thread internally. It's fine to assume that reference equality means that the threats are equal. But that does not mean that if they are different that the objects don't equal semantically. This alone should be enough to advice against the practice of comparing references in business logic.

登录 后发表回答