Как обратиться к потокобезопасности служебных данных, используемых для поддержки статических локальных переменных в C ++? - PullRequest
5 голосов
/ 02 июня 2010

Рассмотрим следующий сценарий. У нас есть функция C ++ со статической локальной переменной:

void function()
{
    static int variable = obtain();
    //blahblablah
}

функция должна вызываться из нескольких потоков одновременно, поэтому мы добавляем критический раздел, чтобы избежать одновременного доступа к статическому локальному:

void functionThreadSafe()
{
    CriticalSectionLockClass lock( criticalSection );
    static int variable = obtain();
    //blahblablah
}

но этого будет достаточно? Я имею в виду магию, которая делает инициализацию переменной не более одного раза. Таким образом, есть некоторые служебные данные, поддерживаемые средой выполнения, которые указывают, была ли инициализирована каждая статическая локальная локальная среда.

Будет ли критическая секция в приведенном выше коде защищать также данные службы? Требуется ли дополнительная защита для этого сценария?

Ответы [ 4 ]

5 голосов
/ 02 июня 2010

C ++ говорит, что ваша статическая переменная должна быть инициализирована только один раз - однако C ++ не работает с потоками (пока).

gcc (по крайней мере в * nix системах) делает правильную магию для безопасной защиты нескольких потоков, инициализирующих такую ​​статическую переменную. Согласно http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/12f8e2c7-d94d-4904-8c79-a0b0d1088e0b, msvc этого не делает - и в этом случае вам придется заблокировать инициализацию самостоятельно.

Защита инициализации с критическим разделом должна защищать все это - т. Е. С вашей функциейThreadSafe () все в порядке - (если obtain() сама не вызывает functionThreadSafe()

http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx стоит прочитать в этом отношении.

Лично, чтобы избежать неожиданностей, я постараюсь переписать это, чтобы вы могли инициализировать variable самостоятельно, один раз, прежде чем создавать какие-либо потоки - например,

static int variable = 0;
void init_variable() //call this once, at the start of main()
{
  variable = obtain();
}

void function() 
{
  //use variable, lock if you write to it
}
1 голос
/ 02 июня 2010

(у меня есть пост на другой вопрос, но это также ответ на этот вопрос)

Вот мое мнение (если вы действительно не можете инициализировать его до запуска потоков):

Я видел (и использовал) нечто подобное для защиты статической инициализации, используя boost :: once

#include <boost/thread/once.hpp>

boost::once_flag flag;

// get thingy
const Thingy & get()
{
    static Thingy thingy;

    return thingy;
}

// create function
void create()
{
     get();
}

void use()
{
    // Ensure only one thread get to create first before all other
    boost::call_once( &create, flag );

    // get a constructed thingy
    const Thingy & thingy = get(); 

    // use it
    thingy.etc..()          
}

В моем понимании, таким образом все потоки ждут на boost :: call_once, кроме одного, который создаст статическую переменную. Он будет создан только один раз и больше никогда не будет вызван. И тогда у тебя больше нет блокировки.

0 голосов
/ 02 июня 2010

Некоторые боковые уклоны, которые вы можете попробовать, могут решить вашу основную проблему:

  • вы можете сделать int variable статическим локальным для потока , если разным потокам на самом деле не нужно передавать значение этой переменной или передавать данные друг другу через нее.
  • для int на x86 вы можете использовать атомарное чтение / запись, например, InterlockedCompareExchange() или его эквивалент на вашей платформе. Это позволяет нескольким потокам безопасно обращаться к переменной без блокировок. Тем не менее, он работает только для аппаратных атомарных типов (например, 32-битных и 64-битных слов). Вам также нужно будет выяснить, что делать, если два потока хотят записать в переменную одновременно (т. Е. Один обнаружит, что другой записал в него запись при выполнении операции сравнения и замены).
0 голосов
/ 02 июня 2010

Чтобы избежать блокировки в любом случае, вы можете пойти с этим:

void functionThreadSafe()
{
    static int *variable = 0;
    if (variable == 0)
    {
       CriticalSectionLockClass lock( criticalSection );
       // Double check for initialization in different thread
       if (variable == 0)
       {
          variable = new int(obtain());
       }
    }
    //blahblablah
}
...