Локальное хранилище потоков для библиотеки классов С#

У меня есть очень старая, но очень большая библиотека, которую я рассматриваю для преобразования в библиотеку классов С#. В существующей библиотеке используется множество глобальных переменных, хранящихся в TLS. В С# нет реальной концепции глобальных переменных, но одним из способов является использование статического класса, называемого чем-то вроде GlobalVar, и размещение их в этом классе, чтобы к ним можно было обращаться через GlobalVar.xxxxxx

Однако, я думаю, что это сломает весь существующий код, который будет преобразован, поскольку класс GlobalVar будет нормальным глобальным классом, а не потоковым хранилищем. Есть ли способ заставить эти глобалы быть в потоке? то есть эквивалент __declspec (thread) static в С#?

В этот момент я должен добавить, что ненавижу глобальные переменные. Я думаю, что они часто являются результатом плохого дизайна. Однако из-за ограниченных временных ограничений первый этап состоит в том, чтобы преобразовать библиотеку в С# с минимальной суматохой, а затем выполнить фазу 2, чтобы перепроектировать их правильно.

Ответ 1

Существует класс ThreadLocal (введен в 4.0) и ThreadStaticAttribute.

ThreadStaticAttribute может использоваться только в static. Класс ThreadLocal может использоваться в "нормальных" полях, но он медленнее.

Имейте в виду, что если вы не контролируете поток, на котором вы находитесь (например, вы являетесь страницей ASP.NET, и вы начинаете с "случайного" предварительно использованного потока, или вы являетесь потоком ThreadPool), то ваши переменные "thread-static" (вообще, а не атрибут) будут предварительно инициализированы старыми значениями предыдущего потока. (см., например, Рассказ о двух методах: Атрибут [ThreadStatic] и System.Web.HttpContext.Current.Items)

Я забыл, есть Thread.AllocateDataSlot, который имеет похожие "цели", чем другие.

Ответ 2

Вы можете добиться того же локального хранилища потоков, используя атрибут [ThreadStatic] или в .Net 4, используя класс ThreadLocal.

[ThreadStatic]    
private static string MyThreadGlobal;

private ThreadLocal<string> MyThreadGlobal = new ThreadLocal<string>();

Также существует класс CallContext, но, вероятно, предпочтительны другие подходы.

Ответ 3

Предполагая, что вы собираетесь использовать .NET 4.0, вы можете иметь static ThreadLocal<ThreadLocalData>, где ваш класс ThreadLocalData имеет все ваши переменные как свойства:

class ThreadLocalData
{
    public int GlobalInt { get; set; }
    public string GlobalString { get; set; }
}

class Global
{
    static ThreadLocal<ThreadLocalData> _ThreadLocal =
        new ThreadLocal<ThreadLocalData>( () => new ThreadLocalData() );

    public static ThreadLocalData ThreadLocal
    {
       get { return _ThreadLocal.Value; }
    }
}

Затем вы получите доступ к следующим свойствам:

int i = Global.ThreadLocal.GlobalInt;

Вы можете добавить любые глобальные переменные, которые не являются нить-локальными, как обычные свойства класса Global.