Мне нужно создать статическую переменную, защищенную потоком, в С#.Net
ok, это немного сложнее, чем вопрос.
class A
{
static int needsToBeThreadSafe = 0;
public static void M1()
{
needsToBeThreadSafe = RandomNumber();
}
public static void M2()
{
print(needsToBeThreadSafe);
}
}
теперь я требую, чтобы между вызовами M1() и M2() "needsToBeThreadSafe" остается Thread Safe.
Ответы
Ответ 1
То, о чем вы можете попросить, это атрибут [ ThreadStatic]. Если вы хотите, чтобы каждый поток, который использует класс A
, имеет собственное значение needsToBeThreadSafe
, вам просто нужно украсить это поле с помощью ThreadStatic].
Для получения дополнительной информации см. документацию MSDN для ThreadStaticAttribute
.
Ответ 2
Как насчет:
public static void M1()
{
Interlocked.Exchange( ref needsToBeThreadSafe, RandomNumber() );
}
public static void M2()
{
print( Interlocked.Read( ref needsToBeThreadSafe ) );
}
Ответ 3
У вас есть два варианта: самым простым с учетом вашего представленного кода является ключевое слово volatile
. объявите needsToBeThreadSafe
как static volatile int
, и это гарантирует, что любой поток, который ссылается на эту переменную, получит "последнюю" копию, а переменная не будет кэшироваться внутри вашего кода.
Если вы хотите в более общем плане обеспечить, чтобы M1()
и M2()
выполнялись "атомарно" (или, по крайней мере, исключительно друг из друга), тогда вы хотите использовать lock
. Самый чистый синтаксис имеет "блокирующий блок", например:
private static object locker = new Object();
//..
public static void M1()
{
lock(locker)
{
//..method body here
}
}
public static void M2()
{
lock(locker)
{
//..method body here
}
}
Что касается того, какой подход взять, что до вас и должен определяться кодом. Если все, что вам нужно, это обеспечить, чтобы назначение участника распространялось на все потоки и не кэшировалось, тогда ключевое слово volatile
проще и сделает работу просто прекрасной. Если это возможно, вы можете пойти с lock
.
Ответ 4
class A
{
static int needsToBeThreadSafe = 0;
static object statObjLocker = new object();
public static void M1()
{
lock(statObjLocker)
{
needsToBeThreadSafe = RandomNumber();
}
}
public static void M2()
{
lock(statObjLocker)
{
print(needsToBeThreadSafe);
}
}
}
Ответ 5
Звучит так, как будто вам нужен Volatile.
static volatile int needsToBeThreadSafe = 0;
Ответ 6
Вы также можете использовать ReaderWriterLockSlim, который более эффективен для нескольких чтений и меньше записей:
static int needsToBeThreadSafe = 0;
static System.Threading.ReaderWriterLockSlim rwl = new System.Threading.ReaderWriterLockSlim();
public static void M1()
{
try
{
rwl.EnterWriteLock();
needsToBeThreadSafe = RandomNumber();
}
finally
{
rwl.ExitWriteLock();
}
}
public static void M2()
{
try
{
rwl.EnterReadLock();
print(needsToBeThreadSafe);
}
finally
{
rwl.ExitReadLock();
}
}
Ответ 7
Для начала я согласен с ответами, используя lock()
, это самый безопасный способ.
Но существует более минималистский подход, ваш пример кода показывает только одиночные утверждения с помощью needsToBeThreadSafe
, и поскольку int
является атомарным, вам нужно только предотвратить кэширование компилятором с помощью volatile:
class A
{
static volatile int needsToBeThreadSafe = 0;
}
Но если вам нужно, чтобы requireToBeThreadSafe был "ThreadSafe" для нескольких операторов, используйте блокировку.