Как гарантировать, что функция не будет введена снова, если она не вернется в потоке? - PullRequest
1 голос
/ 25 марта 2009

Я не хочу, чтобы функция вводилась одновременно несколькими потоками, и при этом я не хочу, чтобы она снова вводилась, когда она еще не вернулась. Есть ли подход к достижению моей цели? Большое спасибо!

Ответы [ 7 ]

8 голосов
/ 25 марта 2009

Обе цели могут быть достигнуты с помощью семафора мьютекса.

3 голосов
/ 25 марта 2009

Используйте критическую секцию (InitializeCriticalSection (), EnterCriticalSection (), LeaveCriticalSection ()), а также реализуйте счетчик записей. Критическая секция защищает от повторного входа из разных потоков, а счетчик входа защищает от повторного входа из одного потока.

Для реализации счетчика записей используйте общую переменную (логическое для вашего случая) и класс скобок. Как только вы уже вошли в критическую секцию (и, следовательно, никакой другой поток не будет выполнять тот же код параллельно), проверьте значение переменной. Если говорится, что функция уже введена - выйдите (сначала отпустите критическую секцию, затем выйдите из функции). В противном случае создайте свой экземпляр класса скобок, который изменит значение переменной. Поэтому, когда в следующий раз этот поток войдет в функцию, он проверит переменную, увидит, что повторный вход произошел, и уйдет. Деструктор класса скобок изменит переменную на ее первоначальное значение после выхода из функции.

Целесообразно использовать скобочные классы как для входа в критическую секцию, так и для изменения счетчика записей, чтобы ваш код был безопасным для исключений, и все действия выполнялись в необходимом порядке и независимо от того, как вы выходите из функции - по исключению или по возвращении заявление.

3 голосов
/ 25 марта 2009

Блокировка функции от входа в другие потоки, когда она выполняется в одном потоке, довольно проста, как объясняют другие ответы. Но если вы хотите, чтобы он блокировался в том же потоке, когда он уже был введен ... ну, это тупик.

1 голос
/ 25 марта 2009

f будет вызываться только при запуске, когда никто другой в данный момент не запускает его. (Это демонстрация концепции только с вызовами Win32)

void f();

err call_f()
{
    static HMUTEX hMutex;
    if( !hMutex )
    {
        hMutex = ::CreateMutex( 0, TRUE, 0 );
    }
    else
    {
        if( WaitForSingleObject( hMutex, 0 ) != WAIT_OBJECT_0 )
            return ERR_ALREADY_RUNNING;
    }

    // calling f here
    f();

    ReleaseMutex( hMutex );
    return S_OK;
}

Остерегайтесь минимальной проверки, отсутствующего кода очистки мьютекса и состояния гонки при первом входе.

1 голос
/ 25 марта 2009

Поскольку вы говорите о C ++ и Windows, взгляните на критические разделы . Вы, вероятно, захотите обернуть его в пару классов C ++, тем не менее, для простоты использования.

Критические разделы пытаются на короткое время вращаться, если блокировка уже снята. Для коротких фрагментов кода это часто позволяет избежать ожидания полного блокирования и, следовательно, накладных расходов в режиме ядра пользователя <> и т. Д.

0 голосов
/ 25 марта 2009

Вы можете сделать что-то вроде этого:

int some_shared_var = 0;
...
for (;some_shared_var != rank;) ;
run_my_function();
some_shared_var++;

rank - номер вашей темы (предположим, что у вас есть темы с номерами от 0 до size-1).

Это всего лишь пример. Реальная реализация будет отличаться. Это зависит от того, какую библиотеку / функции вы хотите использовать для распараллеливания вашего кода (форк, MPI и т. Д.). Но я надеюсь, что это даст вам несколько полезных мыслей.

0 голосов
/ 25 марта 2009

Как правило, вам нужно ввести монитор, например, в Java, добавив ключевое слово «synchronized» в сигнатуру вашего метода.

(Я прав?)

...