Shared\Counter
OxPHP\Shared\Counter to atomowa 64-bitowa liczba całkowita ze znakiem, działająca w obrębie całego procesu i wyspecjalizowana w akumulacji: zliczaniu zdarzeń, sumowaniu delt, obliczaniu sum w ruchomym oknie. Każda operacja jest nieblokująca; dwa workery dodające współbieżnie nigdy nie gubią ani jednego taktu.
Do dowolnego stanu atomowego, który musi synchronizować inną pamięć (automaty skończone, znaczniki wersji, seqlocki, maski bitowych flag), użyj zamiast tego Shared\Atomic.
Przegląd
- Atomowy int64. Zakres
−9_223_372_036_854_775_808 … 9_223_372_036_854_775_807. Przepełnienie zawija się. - Nieblokujący.
addkompiluje się do pojedynczegofetch_add. - Zawsze Relaxed. Operacje są atomowe (żadnych zgubionych taktów, żadnych rozdartych odczytów), ale nie ustanawiają żadnej relacji happens-before z inną pamięcią. Counter to statystyka, a nie punkt synchronizacji — jeśli potrzebujesz uporządkowania, użyj
Shared\Atomic. - Współdzielony. Instancje można przechowywać wewnątrz
Shared\Map/Shared\Channeli przekazywać do Fiberów przez przechwyceniause.
Dokumentacja API
namespace OxPHP\Shared;
final class Counter implements Shareable
{
public function __construct(int $initial = 0);
public function get(): int; // current
public function set(int $value): int; // returns previous; set(0) = window reset
public function add(int $delta = 1): int; // returns new; add()=+1, add(-1)=decrement
public function compareAndSet(int $expect, int $new): bool;
public function id(): int;
}| Metoda | Zwraca | Zastosowanie |
|---|---|---|
get |
bieżąca | Odczyt bez modyfikacji. |
set |
poprzednia | Atomowa wymiana; set(0) to odczyt-i-wyzerowanie na koniec okna. |
add |
nowa | add() zwiększa o 1, add(-1) zmniejsza, w innym wypadku dowolna delta. |
compareAndSet |
bool | Liczniki z ograniczeniem / nasyceniem (limit, dolna granica) przez pętlę CAS. |
id |
id w rejestrze | Logowanie, śledzenie, korelacja /__ox_shared/entry?id=…. |
Przykłady
Licznik żądań na worker
<?php
$requests = new OxPHP\Shared\Counter();
oxphp_worker(function () use ($requests) {
$count = $requests->add(); // +1, returns the new total
header("X-Request-Count: {$count}");
echo "ok";
});Przełączanie okien
<?php
$hits = new OxPHP\Shared\Counter();
// Every N minutes in your cron/worker loop:
$prev = $hits->set(0); // atomically reads and zeroes
logWindowMetric($prev);Licznik z ograniczeniem (pętla CAS)
<?php
$slots = new OxPHP\Shared\Counter();
$cap = 100;
// Claim a slot only while under the cap.
do {
$cur = $slots->get();
if ($cur >= $cap) {
// full — reject
break;
}
} while (!$slots->compareAndSet($cur, $cur + 1));Akumulacja zbiorcza
<?php
$bytes = new OxPHP\Shared\Counter();
// Sum a batch in PHP, then one atomic add (one FFI call).
$deltas = array_map(fn ($req) => strlen($req['body']), $batch);
$newTotal = $bytes->add(array_sum($deltas));Semantyka i pułapki
set() zwraca poprzednią wartość, a następnie zapisuje — atomowo. set(0) to wzorzec migawka-i-zerowanie (LongAdder::sumThenReset); set($n) ustawia dowolny nowy punkt startowy.
Każda operacja jest atomowa, ale Counter nie publikuje innej pamięci. Jeśli czytelnik musi zobaczyć dane, które piszący zapisał przed zwiększeniem liczby całkowitej, to jest synchronizacja — użyj Shared\Atomic z Ordering::Release/Acquire.
compareAndSet działa w trybie Relaxed/Relaxed i nie przyjmuje argumentów uporządkowania. Jest poprawny dla decyzji podejmowanych na podstawie własnej wartości licznika (limit, dolna granica, przejęcie na podstawie wartości). CAS, który publikuje inny stan, należy do Shared\Atomic.
Dodawanie powyżej INT_MAX zawija się do INT_MIN. Dla liczników działających miesiącami z prędkością tysięcy operacji na sekundę utrzymuj wartość w zakresie dziesiątek bilionów lub resetuj ją okresowo.
Brak wartości ułamkowych. Liczysz bajty na potrzeby średnich o precyzji zmiennoprzecinkowej? Śledź licznik (Counter) i mianownik (Counter) osobno i dziel je w momencie odczytu.
Wyjątki
| Wyjątek | Zgłaszany przez |
|---|---|
StaleHandleException |
Dowolną metodę na uchwycie, którego wpis w rejestrze usunięto. |
UninitializedException |
id() na wrapperze, który nie zakończył __construct. |
Liczniki nigdy nie zgłaszają wyjątku przy przepełnieniu ani wartościach skrajnych — zawijają się.
Obserwowalność
Pełny przegląd znajdziesz w Obserwowalności stanu współdzielonego. Szybkie odniesienia:
GET /__ox_shared/entry?id=Nudostępnia{ value, type: "Counter" }.- Miernik Prometheusa
oxphp_shared_counter_value{counter_id="…"}śledzi bieżącą wartość. - Liczniki obejmujące cały rejestr (
oxphp_shared_operations_total,oxphp_shared_objects_total) uwzględniają Counter dzięki etykiecietype="Counter".
Kiedy nie używać
- Liczby zmiennoprzecinkowe lub dziesiętne. Użyj pary liczników Counter (licznik / mianownik) albo
Shared\Mutex<array{total_cents: int, count: int}>. - Zdarzenia nienumeryczne wymagające bogatego kontekstu. Jeśli potrzebujesz
{count, last_actor, last_reason}powiązanego z jednym kluczem, sięgnij poShared\MaplubShared\Mutex. - Sumy między hostami. Counter działa wyłącznie w obrębie procesu. Do agregacji między wieloma hostami użyj potoku metryk (Prometheus +
rate()albo centralneINCRw Redisie). - Trwałość. Stan licznika Counter znika przy zatrzymaniu serwera. Zapisuj migawki gdzie indziej, jeśli suma musi przetrwać restarty.
Powiązane
- Stan współdzielony — przegląd i wzorce migracji.
- Shared\Atomic — uniwersalny atomowy int64 z CAS, swapem i pełną kontrolą uporządkowania pamięci.
- Shared\Map — gdy liczby są kluczowane (
Map<string, Counter>). - Shared\Flag — gdy wartość to po prostu włączony/wyłączony.
- Shared\Mutex — gdy licznik musi być aktualizowany w zgraniu z innymi polami.