Как запустить функцию один раз, и только один раз...?

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

мне интересно, действительно ли это лучший способ?

Я, кажется, помню, что читал, что глобальные переменные плохие, а глобальные булевые переменные еще хуже!

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

в моем начальном наборе переменных...

private var myStatus:Boolean = false;

затем в функции, которая часто вызывается...

if (!myStatus) {
    doMyFunction();
    myStatus = true;
}

это кажется мне довольно логичным, но это правильно?

обновление: ну, основываясь на том, что я узнал из ваших ответов, вместо проверки глобальной булевой переменной, я сначала проверяю, существует ли узел XML (я храню изображения в структуре XML перед записью на диск), и, если это не так, я добавляю новый узел с данными изображения в кодировке base64. Я все еще устанавливаю логический флаг, чтобы позже я мог перезаписать пустое изображение с редактируемыми пользователем данными изображения, если это необходимо. Он отлично работает. Спасибо всем за помощь!

теперь я также чувствую себя более комфортно в использовании этой конкретной (потоковой небезопасной) системы в определенных ситуациях.

7 ответов


когда вы вызываете функцию, она должна делать то, что вы ожидаете от нее, с аргументами, которые вы ей даете. Если вы вызываете функцию дважды точно так же, вы должны ожидать, что эта функция даст вам те же результаты или сделает то же самое.

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


Это действительно зависит от того, что вы имеете в виду. Если ваш код будет вызываться из нескольких потоков, то у вас есть условие гонки, которое могло означать, что doMyFunction может быть вызвано много раз. Это потому, что более одного потока может проверить myStatus, убедитесь, что это false, затем вызовите doMyFunction. Вы можете немного улучшить ситуацию, установив сначала переменную:

if (!myStatus) {
    myStatus = true;
    doMyFunction();
}

но это только сужает окно для проблем, не устраняет его.

в исключить гонки, вам нужен замок.


в C/C++ вы обычно можете сохранить тот факт, что doMyFunction () вызывается только один раз инкапсулируется с помощью статической переменной, например:

void doMyFunction() {
     // gets called only once.
     // side effects go here.
}

void functionThatGetsCalledALot() {
    static bool called = false;
    if (!called) {
        doMyFunction();
        called = true;
    }
    // do more stuff
}

это позволяет избежать использования глобалов, но имеет тот же эффект, и статический var объявляется там, где это актуально, поэтому ясно, что происходит. Обратите внимание, что это не потокобезопасно, поэтому вам понадобится блокировка, Если у вас есть потоки.


Это не потокобезопасными. Это может не иметь значения для вас, но вы сказали "язык-агностик": вы, вероятно, не захотите использовать этот шаблон в универсальной библиотеке для Java.

на этот вопрос трудно ответить языком-агностиком, потому что доступные альтернативы очень сильно зависят от языка. Например, в POSIX у вас есть pthread_once, если вам нужна потокобезопасность. В C у вас есть статические локальные переменные, чтобы получить это логическое значение из глобальной области. В любом OO-язык, если вы делаете снимок " чего-то "для последующего использования, тогда может быть подходящий объект, соответствующий" чему-то " (или снимку), который может хранить флаг.


Я не вижу ничего плохого в вашем подходе здесь. Имейте в виду, что не всегда "правильно"...есть определенно неправильные вещи, но то, что правильно, может быть субъективным, а также может сильно зависеть от требований системы.


Я не вижу другого пути. Единственное что приходит на ум о том, как улучшить это - потокобезопасность. Но это необходимо только если у вас есть несколько потоков вызова функции.


в Perl 5.10 или более поздней версии вы будете использовать state переменной.

use 5.010;

sub test{
  state $once = 1;

  if( $once ){
    $once = undef;
    say 'first';
  } else {
    say 'not first';
  }
}

test for 1..5;

выходы

first
not first
not first
not first
not first