Я уже пытался это найти, но ничего не нашел и не знаю, как это будет называться. Логическое объединение?
У меня есть несколько методов, вызываемых параллельно, каждый из которых возвращает логическое значение, указывающее на сбой. Есть ли способ присвоить каждому из них одно логическое значение, чтобы, если какой-либо из них возвращал true, общее логическое значение правда.
ИЕ:
Boolean exceptionOccurred = false;
Parallel.Invoke(
() => exceptionOccurred = MethodA(),
() => exceptionOccurred = MethodB(),
() => exceptionOccurred = MethodC(),
() => exceptionOccurred = MethodD(),
);
с этим кодом как есть, если метод A вернул true, а остальные методы вернули false, в зависимости от того, какой из них вернулся первым/последним, ему почти всегда будет присвоено значение false. Но я хочу знать, вернет ли кто-нибудь из них истину.
Одним из решений является присвоение каждому уникальному логическому значению, а затем проверка всех логических значений. Но я надеялся на что-то вроде побитового оператора bool +=, который не устанавливал бы дляExceptionOccurred значение false, если оно уже было истинным. Есть ли способ сделать это?
Я понимаю, что, вероятно, мог бы расширить логическое значение, чтобы реализовать это самостоятельно, мне просто интересно, есть ли уже способ сделать это?
Я также понимаю, что лучшим решением для каждого метода является создание исключений и их всплеск, но есть причины, по которым мне нужно обращаться с этим по-другому, главным образом потому, что я хочу, чтобы выполнение продолжалось, но в конечном итоге регистрировало общий сбой в конце.
Вы можете использовать то, что называется короткое замыкание. Если значение, которое вы хотите сохранить, равно true
, вам следует использовать оператор or.
bool exceptionOccurred = false;
exceptionOccurred = exceptionOccurred || methodA();
exceptionOccurred = exceptionOccurred || methodB();
Если вы хотите сохранить вывод false
, вам также необходимо использовать оператор and, а также начинать с true
.
bool noExceptionOccurred = true;
noExceptionOccurred = noExceptionOccurred && methodA();
noExceptionOccurred = noExceptionOccurred && methodB();
Я не думаю, что мы избежим дорогостоящих вычислений, если все они будут выполняться одновременно и параллельно. Но да, это будет свидетельствовать об общем провале.
Я совершенно упустил из виду, что они работают параллельно. Да, вы правы, я редактирую свой ответ.
Это имеет состояние гонки и может привести к возникновению исключений, пока exceptionOccurred
имеет значение false.
«Что является сокращением» здесь неверно (ни в одном из ваших примеров). exceptionOccurred |= methodA()
всегда будет выполняться methodA()
, независимо от значения exceptionOccurred
— это не короткое замыкание.
Вы можете реализовать потокобезопасное решение, воспользовавшись Interlocked.Increment
(см. https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.increment?view=net-8.0). .
Вместо логического значения используйте целое число. Инициализируйте его равным нулю и увеличивайте его, если обнаружите ошибку. Итак, что-то вроде этого:
int errors = 0;
Parallel.Invoke(
() => if (MethodA()) { Interlocked.Increment(ref errors); },
() => if (MethodB()) { Interlocked.Increment(ref errors); },
() => if (MethodC()) { Interlocked.Increment(ref errors); },
() => if (MethodD()) { Interlocked.Increment(ref errors); },
);
Boolean exceptionOccurred = (errors == 0);
Не хватает ref
в Interlocked.Increment(ref errors)
.
Параллельная запись в одно и то же место назначения безопасна/полезна только в том случае, если в место назначения всегда записывается один и тот же результат. К счастью, ваш случай — одно из таких исключений. Таким образом, следующее является потокобезопасным и укажет, произошел ли сбой хотя бы одного метода. Он также самый быстрый, поскольку не требует синхронизации:
() =>
{
var failure = MethodA();
if (failure) exceptionOccurred = true;
}
a |= b
или a = a || b
небезопасно. Учтите следующее:
Thread1 reads a [false]
Thread1 reads b [true]
Thread2 reads a [false]
Thread2 reads b [false]
Thread1 writes true
Thread2 writes false
exceptionOccurred |= MethodA()