Как вызвать функцию один раз и только один раз ...? - PullRequest
6 голосов
/ 10 декабря 2008

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

Мне интересно, действительно ли то, как я это делаю, лучше?

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

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

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

private var myStatus:Boolean = false;

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

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

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

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

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

Ответы [ 7 ]

7 голосов
/ 10 декабря 2008

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

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

5 голосов
/ 10 декабря 2008

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

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

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

Чтобы устранить условие гонки, вам нужен замок.

4 голосов
/ 10 декабря 2008

В 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
}

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

2 голосов
/ 10 декабря 2008

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

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

0 голосов
/ 10 декабря 2008

В 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
0 голосов
/ 10 декабря 2008

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

0 голосов
/ 10 декабря 2008

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

...