views:

144

answers:

6
+2  Q: 

Thread Renaming

In Java, renaming threads is possible. In .NET it is not. This is because the Name is a property that is write-once in the Thread class:

public string Name
{
    get
    {
        return this.m_Name;
    }
    [HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
    set
    {
        lock (this)
        {
            if (this.m_Name != null)
            {
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WriteOnce"));
            }
            this.m_Name = value;
            InformThreadNameChangeEx(this, this.m_Name);
        }
    }
}

Given the fact that Java allows thread renaming and most of the underlying thread structures used are OS-supplied in both platforms, I'm inclined to think that I could actually rename a thread in C#, if I avoid a certain set of functionality that a) I don't care or b) don't use at all.

Do you have any idea why Thread renaming is a write-once operation? Any idea if changing the name breaks something?

I have tried a test where I rename the thread as such:

var t1 = new Thread(TestMethod);
t1.Name = "abc";
t1.Start();
t1.GetType().GetField("m_Name", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(t1, "def");
t1.GetType().GetMethod("InformThreadNameChangeEx", BindingFlags.NonPublic | BindingFlags.Static).Invoke(t1, new object[] { t1, t1.Name});

The result is that the name is indeed changed and this is reflected on other code that uses this thread. The background to this is that I need to log things that threads do and the logging library I use (log4net) uses Thread.Name to specify which thread does what action. Thanks in advance.

EDIT: Please stop suggesting obvious things! I know how to name a thread at start if I am asking how to RE-name it.

The reason why I need to do this, is that the thread will be re-used and it may be used by another component and I want to signify this, if and when there will be logging occuring, so as to have a specific thread name, rather than a generic number.

A: 

Thread names in .NET (and Java) are used purely for debugging and diagnostics purposes. While the logic that because Java can rename its threads that .NET can do the same is faulty (because a .NET thread is a wrapper over a system thread with additional functionality, as is a Java thread, but they're otherwise unrelated), there's no harm per se in changing the thread's name, other than risking breakage in future versions since you're using a non-public API.

However, what reason do you have for changing it? I think it was made read-only to avoid the creation of "kitchen sink" threads that do all manner of tasks. While there are exceptions, of course, I would caution you to consider whether or not a design that requires this is the right design.

Adam Robinson
Yes, they'd do better to add a thread-local label or something.
Steven Sudit
@Adam: It's not my design decision, log4net uses Thread.Name to log the current thread's name. So if I want to log a meaningful name I have to rename the thread, as threads are re-used in different components and I want to signify which.
_NT
@_NT: log4net is open-source. If it does something inconvenient, fix it.
Steven Sudit
@_NT: How is the thread "re-used"? A thread is initialized with a single function, and the name can be set at that time, as you know. You need to be clearer on what, exactly, constitutes "reusing" a thread.
Adam Robinson
@Steven: I realize that but I don't think they will accept my change, as it works for 99% of the code out there now. Having a private branch would mean merging on every new version which can be quite a bit of work.
_NT
@Adam: There is a single function indeed, but the body is the execution of a passed Action, so that could be anything.
_NT
@_NT: I see; I think that's the sort of "kitchen sink" approach that was trying to be discouraged. What is the purpose of having this all in one thread?
Adam Robinson
@Adam: A threadpool alternative to System.Threading.Threadpool. It is simply thread re-use to avoid the overhead of creation.
_NT
_NT: Uhm, given that .NET 4.0's parallel extensions use the ThreadPool, why are you reinventing this wheel?
Steven Sudit
+5  A: 

Threads, at the OS level, don't have names. Really, it's just a convenience feature.

Steven Sudit
+1 Thanks very much for pointing this out!
_NT
A: 

There is the definite possibility that something relies on the name being immutable, which is what the design is mandating. By breaking this encapsulation you run the risk of breaking something that (in good faith) relies on this. I cannot give a concrete example, but since this property can be used in any conceivable way, the problem is unbounded. As a theoretical example something could use the thread's name as a key in a collection.

I would love to know if there was a drop-dead concrete example though as I too use log4net and see what you are driving at. :)

Update

This has certainly piqued my interest as a log4net user. Not wanting to maintain a fork of log4net, this is a possible solution that is safer.

  1. Write a wrapper for log4net i.e. an ILog type interface (I already have one and 15 minutes work).

  2. Use a thread local variable technique to record a thread name (e.g. with an Extension method Thread.LoggingName = "blah blah blah") at entry points to your components.

  3. In your logging wrapper temporarily change the thread name and then change it back again after logging.

This takes care of re-naming threads and handles re-naming them back again, so that if something that does not name threads logs out it will not log out with an incorrect name.

Update 2

A rough example of the technique:

public class MyComponent
{
    public void EntryPoint()
    {
        MyLogger.CurrentLoggerThreadName = "A thread contextual name.";

        _myLogger.Info("a logging message.");

        SomeOtherMethod();
    }

    private void SomeOtherMethod()
    {
        _myLogger.Info("another logging message with the same thread name.");
    }
}

public class MyLogger
{
    [ThreadStatic]
    private static string _CurrentLoggerThreadName;

    private static readonly FieldInfo NameField = typeof(Thread).GetType().GetField("m_Name", BindingFlags.Instance | BindingFlags.NonPublic);

    public static string CurrentLoggerThreadName
    {
        get { return _CurrentLoggerThreadName; }
        set { _CurrentLoggerThreadName = value; }
    }

    private static void LogWithThreadRename(Action loggerAction)
    {
        Thread t1 = Thread.CurrentThread;

        string originalName = (string)NameField.GetValue(t1);

        try
        {
            NameField.SetValue(t1, CurrentLoggerThreadName);
            loggerAction();
        }
        finally
        {
            NameField.SetValue(t1, originalName);
        }
    }

    public void Info(object message)
    {
        LogWithThreadRename(() => _iLog.Info(message));
    }

    //More logging methods...
}
chibacity
@chibacity: I realize that this is as clear an indicator as you get from the programmers working on System.Threading that the naming if (according to their understanding) very important. But I wonder if anyone knows exactly what depends on this? (Mono developers I'm looking at you)
_NT
@_NT It is not just framework stuff I'm thinking about. It's a public property so anything could take a dependency on it e.g. other frameworks and tools you use.
chibacity
Very interesting update. Can you please elaborate on point 2, what is the thread local variable technique you have in mind. And in 3, do you mean by changing the backing field "m_Name" only (i.e. without calling the native notification method) and then setting it back? The last sounds harmless enough, although reliant on the field name.
_NT
@_NT Please see my update. Yes I am suggesting changing only the name and then changing it back again. This should be sufficient for log4net to log out the required logging thread name.
chibacity
+4  A: 

I would not change the name of the thread in the manner that you are. While you are doing the same thing that a write-many operation would require, you don't know that there aren't behaviors dependent on the operation being write-once.

For example, the debugger, if it gets the thread name, can cache that name, and not have to call the object anymore for it.

There is also a design question here, of why you are depending on the thread name to help with your logging; you are relying on the behavior of the logger to indicate part of the operation you are trying to log.

If you want to capture a certain semantic, you shouldn't shape the logger and the thread to conform to a pattern which will capture that semantic. Instead, explicitly indicate the semantic to the logger at the particular point in time.

casperOne
@casperOne: It is clear to me (and I've tried to explain this in the question above) that this is an indicator from the programmers working on System.Threading that this should better not be tampered with. But does anyone know exactly what depends on this?
_NT
@_NT: It's like asking what depends on GetType(). It's a publicly available property on a type, it's nearly impossible to say what depends on it or what assumptions are made because of the fact it is write-once.
casperOne
@casperOne: See Brian Gideon's answer!
_NT
@_NT: I did, and unfortunately, it's wrong. You are assuming that the BCL is the only consumer of the Name property on the Thread class, which is an incorrect assumption; it is a public property on a publicly accessible type. A good example, is the Thread drop down in the debug window in VS.NET, it uses the Thread.Name property, but the assemblies that do that are not in the BCL. You are also coding against the implementation and not the contract. The contract of the property says it is write-once, you should abide by that, or pay the consequences when the implementation changes.
casperOne
A: 

Changing the name, or attempting to change the name, could well break something. If the implementation of System.Threading.Thread changes so that the field m_Name is called m_ThreadName, for example, in a future version of the .NET Framework, or indeed a service pack or hot-fix (unlikely though that may be), your code will throw an exception.

Rob
Indeed, but I don't plan to use the above, I'm simply checking if it works without failing something immediately: It does work at this -VERY- experimental level.
_NT
+1  A: 

I used the analyze operation from Reflector and the only code in the BCL that I saw (or more precisely Nikolaos saw) which uses the Thread.Name getter was a call to the RegisterClassEx API in user32.dll. The Thread class itself only refers to the m_Name member in the Name getter and setter. I suspect it is safe to rename the thread in the manner you have adopted. Except that I would change your code to acquire a lock on the same object that Thread.Name would have. Fortunately that is none other than the Thread instance itself so it is easy to do.

var t1 = new Thread(TestMethod); 
t1.Name = "abc"; 
t1.Start(); 
lock (t1) 
{
  t1.GetType().
      GetField("m_Name", BindingFlags.Instance | BindingFlags.NonPublic).
      SetValue(t1, "def"); 
  t1.GetType().
      GetMethod("InformThreadNameChangeEx", BindingFlags.NonPublic | 
          BindingFlags.Static).
      Invoke(t1, new object[] { t1, t1.Name});
}

Another thing to note is that you might have problems with code access security and changing private members depending on what trust level the application has. Obviously that did not seem to come into play with your test, but it is worth mentioning here.

Brian Gideon
I also ran the analysis and it showed me that MS.Win32.HwndWrapper..ctor(...) uses the Thread.Name getter. It uses it to set the "string lpszClassName" of a new Window...
Nikolaos
@Nikolaos: Yep, nice catch. I am not sure how I missed that the first time.
Brian Gideon