Мне нужно создать потокобезопасную статическую переменную в C # .Net - PullRequest
11 голосов
/ 10 августа 2009

хорошо, это немного сложнее, чем вопрос.

class A
{
   static int needsToBeThreadSafe = 0;

   public static void M1()
   {
     needsToBeThreadSafe = RandomNumber();
   }

   public static void M2()
   {
     print(needsToBeThreadSafe);
   }
}

теперь мне требуется, чтобы между вызовами M1 () и M2 () «needsToBeThreadSafe» оставался безопасным для потоков.

Ответы [ 7 ]

21 голосов
/ 19 ноября 2010

Как насчет:

public static void M1()
{
    Interlocked.Exchange( ref needsToBeThreadSafe, RandomNumber() );
}

public static void M2()
{
    print( Interlocked.Read( ref needsToBeThreadSafe ) );
}
19 голосов
/ 10 августа 2009

То, о чем вы можете пытаться спросить, это атрибут [ ThreadStatic ]. Если вы хотите, чтобы каждый поток, использующий класс A, имел свое собственное отдельное значение needsToBeThreadSafe, вам просто нужно украсить это поле с помощью [ ThreadStatic ] атрибут.

Подробнее см. В документации MSDN для ThreadStaticAttribute.

8 голосов
/ 10 августа 2009

У вас есть два варианта: самый простой из представленных вами кодов - это ключевое слово 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 голосов
/ 10 августа 2009
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);
       }
   }
}
2 голосов
/ 10 августа 2009

Для начала я согласен с ответами, используя lock(), это самый безопасный способ.

Но существует более минималистский подход, ваш пример кода показывает только отдельные операторы, использующие needsToBeThreadSafe, и, поскольку int является атомарным, вам нужно только предотвратить кеширование компилятором с использованием volatile:

class A
{
   static volatile int needsToBeThreadSafe = 0;

}

Но если вам нужно, чтобы needsBeThreadSafe был «ThreadSafe» для нескольких операторов, используйте блокировку.

2 голосов
/ 10 августа 2009

Вы также можете использовать 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();
    }
}
2 голосов
/ 10 августа 2009

Звучит так, будто вам нужен член Volatile .

static volatile int needsToBeThreadSafe = 0;
...