Переименование потока

В Java переименование распараллеливает, возможно. В.NET это не. Это вызвано тем, что Имя является свойством, которое является неперезаписываемым в классе Потока:

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);
        }
    }
}

Учитывая тот факт, что Java позволяет переименование потока, и большинство базовых используемых структур потока предоставляется ОС в обеих платформах, я склонен думать, что я мог на самом деле переименовать поток в C#, если я избегаю определенного набора функциональности что a) я не забочусь или b) не использует вообще.

У Вас есть какая-либо идея, почему переименование Потока является неперезаписываемой операцией? Какая-либо идея, изменив имя повреждает что-то?

Я попробовал тест, где я переименовываю поток как таковой:

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});

Результат состоит в том, что имя действительно изменилось, и это отражается на другом коде, который использует этот поток. Предпосылки к этому - то, что я должен зарегистрировать вещи, которые потоки делают и регистрирующаяся библиотека, я использую (log4net) Поток использования. Имя для определения, который поток делает что действие.Заранее спасибо.

Править: Прекратите предлагать очевидные вещи! Я знаю, как назвать поток в запуске, если я спрашиваю, как Переименовать его.

Причина, почему я должен сделать это, состоит в том, что поток будет снова использован, и он может использоваться другим компонентом, и я хочу показать это, если и когда там будет регистрировать появление, чтобы иметь определенное имя потока, а не универсальное число.

14
задан John Saunders 28 July 2010 в 14:06
поделиться

6 ответов

Я использовал операцию анализа из Reflector и единственный код в BCL, который я видел (точнее, Николаос видел), который использует Thread.Name геттер был вызовом RegisterClassEx API в user32.dll. Сам класс Thread относится только к члену m_Name в геттере и сеттере Name . Я подозреваю, что можно безопасно переименовать поток, как вы выбрали. За исключением того, что я бы изменил ваш код, чтобы получить блокировку того же объекта, что и у Thread.Name . К счастью, это не что иное, как сам экземпляр Thread , так что это легко сделать.

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});
}

Также следует отметить, что у вас могут возникнуть проблемы с безопасностью доступа к коду и изменением закрытых членов в зависимости от того, какой уровень доверия имеет приложение. Очевидно, это не повлияло на ваш тест, но об этом стоит упомянуть здесь.

6
ответ дан 1 December 2019 в 10:32
поделиться

Имена потоков в .NET (и Java) используются исключительно для целей отладки и диагностики. Хотя логика, что, поскольку Java может переименовывать свои потоки, .NET может делать то же самое, ошибочна (поскольку файл.NET-поток - это оболочка над системным потоком с дополнительной функциональностью, как и поток Java, но в остальном они не связаны), нет никакого вреда как такового в изменении имени потока, кроме риска поломки в будущем версий, так как вы используете непубличный API.

Однако по какой причине вы его меняете? Я думаю, что он был сделан только для чтения, чтобы избежать создания потоков "кухонной раковины", которые выполняют всевозможные задачи. Конечно, есть исключения, но я бы посоветовал вам подумать о том, является ли дизайн, который требует этого, правильным.

1
ответ дан 1 December 2019 в 10:32
поделиться

У потоков на уровне ОС нет имен. На самом деле, это просто удобство.

10
ответ дан 1 December 2019 в 10:32
поделиться

Существует определенная вероятность того, что что-то полагается на неизменность имени, что и требует дизайн. Нарушая эту инкапсуляцию, вы рискуете сломать то, что (добросовестно) на нее полагается. Я не могу привести конкретный пример, но поскольку это свойство можно использовать любым мыслимым образом, проблема безгранична. В качестве теоретического примера что-то может использовать имя потока в качестве ключа в коллекции.

Я хотел бы знать, есть ли офигительный конкретный пример, поскольку я тоже использую log4net и смотрю, к чему вы клоните. :)

Обновление

Это определенно вызвало у меня интерес как пользователя log4net. Не желая поддерживать вилку log4net, это более безопасное возможное решение.

  1. Напишите оболочку для log4net, т.е. интерфейс типа ILog (у меня уже есть одна и 15 минут работы).

  2. Используйте технику локальной переменной потока для записи имени потока (например, с помощью метода расширения Thread.LoggingName = "бла-бла-бла") в точках входа в ваши компоненты.

  3. В оболочке журналирования временно измените имя потока, а затем снова измените его после входа в систему.

Это заботится о переименовании потоков и переименовании их снова, так что если что-то, что не называет потоки, выходит из системы, оно не выходит из системы с неправильным именем.

Обновление 2

Примерный пример техники:

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...
}
0
ответ дан 1 December 2019 в 10:32
поделиться

Изменение имени, или попытка изменить имя, вполне может что-то сломать. Если реализация System.Threading.Thread изменится таким образом, что поле m_Name будет называться m_ThreadName, например, в будущей версии .NET Framework, или даже в пакете обновлений или исправлении (хотя это и маловероятно), ваш код выдаст исключение.

0
ответ дан 1 December 2019 в 10:32
поделиться

Я бы не стал менять название потока таким образом, как это делаете вы. Хотя вы делаете то же самое, что потребовала бы операция write-many, вы не знаете, что не существует поведения, зависящего от того, что операция write-once.

Например, отладчик, получив имя потока, может кэшировать это имя и больше не вызывать объект для его получения.

Здесь также возникает вопрос дизайна, почему вы зависите от имени потока, чтобы помочь с протоколированием; вы полагаетесь на поведение регистратора, чтобы указать часть операции, которую вы пытаетесь протоколировать.

Если вы хотите зафиксировать определенную семантику, вам не следует формировать регистратор и поток в соответствии с шаблоном, который позволит зафиксировать эту семантику. Вместо этого явно укажите семантику регистратору в конкретный момент времени.

4
ответ дан 1 December 2019 в 10:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: