Локальное хранилище потоков для библиотеки классов С#
У меня есть очень старая, но очень большая библиотека, которую я рассматриваю для преобразования в библиотеку классов С#. В существующей библиотеке используется множество глобальных переменных, хранящихся в 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
.