Как получить идентификатор текущего потока без AppDomain.GetCurrentThreadId(), чтобы он действительно работал?

Поскольку AppDomin.GetCurrentThreadId() устарел

«AppDomain.GetCurrentThreadId устарел, поскольку он не предоставляет стабильный идентификатор, когда управляемые потоки выполняются на волокнах (также называемых облегченными потоками). Чтобы получить стабильный идентификатор для управляемого потока, используйте свойство ManagedThreadId в потоке. http://go.microsoft.com/fwlink/?linkid=14202"

Я старался не использовать его. Однако объяснение того, что «Thread.CurrentThread.ManagedThreadId» будет работать, бесполезно, потому что оно не предоставляет идентификатор потока Win32, который мне нужен для вызовов Win32. Поэтому я реализовал это сам следующим образом.

public sealed class AppDomainExtender
{
    public static int GetCurrentThreadId_New()
    {
        return Process.GetCurrentProcess().Threads.OfType<ProcessThread>().SingleOrDefault(x => x.ThreadState == System.Diagnostics.ThreadState.Running).Id;
    }
}

Теперь есть две проблемы.

  • Если бы это сработало, разве это не было бы точно так же, как AppDomain.GetCurrentThreadId?
  • Это не работает по той причине, что работающий поток может измениться при прохождении цикла SingleOrDefault. Это странное поведение, но оно только что привело меня к InvalidOperationException ("Последовательность содержит более одного элемента"). По-видимому, это происходит, например, при использовании нескольких рабочих столов (я предполагаю, что это возможно только путем копирования потока, выполняющего цикл, на другой идентификатор).

Вопросы:

  1. Является ли AppDomain.GetCurrentThreadId ложно объявленным устаревшим?
  2. Как можно сделать оператор «Process.GetCurrentProcess().Threads.OfType().SingleOrDefault(x => x.ThreadState == System.Diagnostics.ThreadState.Running).Id» атомарным, или это вообще возможно?

person xamid    schedule 26.08.2014    source источник
comment
Вы пытались PInvoke метод GetCurrentThreadId()?   -  person timothy.B    schedule 26.08.2014
comment
Я реализую приложение для автоматизации пользовательского интерфейса с несколькими рабочими столами на основе скриншотов в Windows 7, существует так много необходимого использования user32.dll, gdi32.dll и kernel32.dll, что его нельзя было упустить. Разве PIvoked 'GetCurrentThreadId()' не делает то же самое, что и 'AppDomain.GetCurrentThreadId()'?   -  person xamid    schedule 26.08.2014
comment
pinvoke.net/default.aspx/kernel32.getcurrentthreadid   -  person timothy.B    schedule 26.08.2014


Ответы (1)


Устаревание AppDomain.GetCurrentThreadId() — неудобная правда, а не «ложное» объявление. Начиная с .NET 2.0 связь между потоком .NET и потоком операционной системы была нарушена. Пользовательский узел CLR может свободно реализовывать многопоточность так, как считает нужным, реализуя Интерфейс IClrTask. Другими словами, у вас нет гарантии, что два разных потока .NET используют разные потоки операционной системы с разными идентификаторами потоков.

В целом это было популярно в 1990-х и начале 2000-х годов, когда использовались такие названия, как «облегченные потоки», сопрограммы, волокна, «зеленые потоки» и т. д. Идея заключалась в том, чтобы избежать переключения контекста потока с помощью операционная система и переключение контекста вместо этого регистрирует ЦП в программном обеспечении. SQL Server был основным бенефициаром этой функции, он имеет встроенную поддержку «волоконного режима».

Хотя команда SQL Server получила эту функцию, они в конечном итоге решили не выпускать ее. Они отказались от проекта, когда не смогли сделать его достаточно стабильным. Проблема, которая, вероятно, вдохновила эту статью, опубликованную в том же год, когда их проект должен был состояться. Это больше не пробовали. Не в последнюю очередь потому, что эта функция устарела благодаря многоядерной революции. В стоимости переключения контекста в современных ядрах доминирует состояние кешей ЦП, конкурировать с каждым ядром, имеющим свой кэш L1 и L2, невозможно.

Я не знаю ни одного хоста CLR, который на самом деле реализует IClrTask, сбой команды SQL Server, безусловно, был большим красным флагом. Это, однако, не исключает возможности сценария, о котором вы, возможно, захотите беспокоиться, — это запуск вашего кода в контексте большого неуправляемого приложения, которое поддерживает сценарии на управляемом языке. Программа CAD была бы типичным примером этого.

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

person Hans Passant    schedule 26.08.2014
comment
Это было очень интересно, спасибо! Хотя, на мой взгляд, волокна .Net на самом деле не являются потоками, и поэтому «GetCurrentThreadId()» не должен быть помечен как устаревший. Кроме того, эти волокна не следует называть потоками, они действуют просто как сопрограммы. - person xamid; 26.08.2014
comment
Это всего лишь мнение, оно не меняет сути. Вам нужно обратиться в Microsoft с для внесения необходимых изменений. . - person Hans Passant; 26.08.2014
comment
На самом деле команда CLR хотела поддерживать NT Fibers для 2.0, но в конечном итоге не смогла этого сделать, см. Duffy's Concurrent Programming on Windows p449. - person Chris O; 16.07.2015