Przeszukałem książkę Swift, ale nie mogę znaleźć wersji Swift @synchronized. Jak mogę dokonać wzajemnego wykluczenia w Swift?
concurrency
mutex
swift
Rachunek
źródło
źródło
removeFirst()
?Odpowiedzi:
Możesz użyć GCD. Jest trochę bardziej szczegółowy
@synchronized
, ale działa jako zamiennik:źródło
Szukałem tego sam i doszedłem do wniosku, że nie ma jeszcze natywnej konstrukcji w swift.
Zrobiłem tę małą funkcję pomocnika na podstawie kodu, który widziałem od Matta Bridgesa i innych.
Użycie jest dość proste
Znalazłem z tym jeden problem. Przekazywanie tablicy jako argumentu blokady wydaje się w tym momencie powodować bardzo tępy błąd kompilatora. W przeciwnym razie wydaje się, że działa zgodnie z oczekiwaniami.
źródło
@synchronized
ładnie zachowuje składnię bloku, ale należy pamiętać, że nie jest on identyczny z rzeczywistą wbudowaną instrukcją bloku, taką jak@synchronized
blok w Objective-C, ponieważ instrukcjereturn
ibreak
nie działają już tak, aby wyskakiwały z otaczającej funkcji / pętli jak byłoby, gdyby było to zwykłe stwierdzenie.defer
słowa kluczowego, aby zapewnić, żeobjc_sync_exit
zostanie wywołany, nawet jeśliclosure
wyrzuca.Podobają mi się i używam wielu odpowiedzi tutaj, więc wybiorę tę, która najbardziej Ci odpowiada. To powiedziawszy, metoda, którą wolę, gdy potrzebuję czegoś takiego jak cel-c,
@synchronized
używadefer
instrukcji wprowadzonej w swift 2.Zaletą tej metody jest to, że krytyczna sekcja może opuścić blok zawierający w jakikolwiek sposób pożądany (np
return
,break
,continue
,throw
) oraz „sprawozdanie w zestawieniu odroczyć są realizowane bez względu na to w jaki sposób program kontroli jest przekazywana”. 1źródło
lock
? Jak jestlock
inicjowany?lock
jest dowolnym obiektem celu-c.Można wstawiać instrukcje między
objc_sync_enter(obj: AnyObject?)
iobjc_sync_exit(obj: AnyObject?)
. Słowo kluczowe @synchronized używa tych metod pod przykryciem. to znaczyźródło
objc_sync_enter
iobjc_sync_exit
są metodami zdefiniowanymi w Objc-sync.h i są open source: opensource.apple.com/source/objc4/objc4-371.2/runtime/…objc_sync_enter(…)
iobjc_sync_exit(…)
są nagłówkami publicznymi udostępnianymi przez iOS / macOS / etc. Interfejsy API (wygląda na to, że znajdują się….sdk
na ścieżceusr/include/objc/objc-sync.h
) . Najłatwiejszym sposobem sprawdzenia, czy coś jest publicznym interfejsem API, jest (w Xcode) wpisanie nazwy funkcji (np.objc_sync_enter()
Argumenty nie muszą być określone dla funkcji C) , a następnie spróbuj kliknąć ją. Jeśli pokazuje plik nagłówka dla tego interfejsu API, oznacza to, że jesteś dobry (ponieważ nie byłby on w stanie zobaczyć nagłówka, gdyby nie był publiczny) .Analogicznie do
@synchronized
dyrektywy z Objective-C może mieć dowolny typ zwrotu i ładnerethrows
zachowanie w Swift.Zastosowanie
defer
instrukcji pozwala bezpośrednio zwrócić wartość bez wprowadzania zmiennej tymczasowej.W Swift 2 dodaj
@noescape
atrybut do zamknięcia, aby umożliwić więcej optymalizacji:Na podstawie odpowiedzi z GNewc [1] (gdzie lubię dowolny typ zwrotu) i Tod Cunningham [2] (gdzie lubię
defer
).źródło
SWIFT 4
W Swift 4 możesz używać kolejek dyspozytorskich GCD do blokowania zasobów.
źródło
.serial
wydaje się być niedostępny. Ale.concurrent
jest dostępny. : /myObject.state = myObject.state + 1
działałbyś jednocześnie, nie policzyłby wszystkich operacji, ale zamiast tego dałby wartość niedeterministyczną. Aby rozwiązać ten problem, kod wywołujący powinien być zawinięty w kolejkę szeregową, aby zarówno odczyt, jak i zapis odbywały się atomowo. Oczywiście Obj-c@synchronised
ma ten sam problem, więc pod tym względem Twoja implementacja jest poprawna.myObject.state += 1
jest kombinacją operacji odczytu, a następnie operacji zapisu. Niektóre inne wątki wciąż mogą pojawiać się między nimi, aby ustawić / zapisać wartość. Zgodnie z objc.io/blog/2018/12/18/atomic-variables łatwiej byłoby uruchomićset
blok synchronizacji / zamknięcia, a nie pod samą zmienną.Aby dodać funkcję zwrotu, możesz to zrobić:
Następnie możesz zadzwonić za pomocą:
źródło
Korzystając z odpowiedzi Bryana McLemore'a, rozszerzyłem ją, aby obsługiwała obiekty rzucające się do bezpiecznej posiadłości dzięki zdolności odroczenia Swift 2.0.
źródło
rethrows
aby uprościć korzystanie z zamknięciami nie rzucającymi (nie trzeba używaćtry
), jak pokazano w mojej odpowiedzi .Szybki 3
Ten kod ma możliwość ponownego wprowadzania i może współpracować z wywołaniami funkcji asynchronicznych. W tym kodzie, po wywołaniu someAsyncFunc (), inne zamknięcie funkcji w kolejce szeregowej będzie przetwarzane, ale będzie blokowane przez semaphore.wait (), dopóki nie zostanie wywołane signal (). InternalQueue.sync nie powinien być używany, ponieważ zablokuje główny wątek, jeśli się nie mylę.
objc_sync_enter / objc_sync_exit nie jest dobrym pomysłem bez obsługi błędów.
źródło
W sesji „Zrozumienie awarii i dzienników awarii” 414 WWDC w 2018 r. Pokazują one następujący sposób przy użyciu DispatchQueues z synchronizacją.
W swift 4 powinno być coś takiego:
W każdym razie możesz także przyspieszyć odczytywanie, używając równoległych kolejek z barierami. Odczyty synchronizacji i asynchroniczne są wykonywane jednocześnie, a zapisanie nowej wartości czeka na zakończenie poprzednich operacji.
źródło
Użyj NSLock w Swift4:
źródło
W nowoczesnym Swift 5 z funkcją powrotu:
Użyj go w ten sposób, aby skorzystać z możliwości zwracania wartości:
Lub tak inaczej:
źródło
GCD
). Wydaje się, że w zasadzie nikt nie używa ani nie rozumie, jak używaćThread
. Jestem z tego bardzo zadowolony - podczas gdyGCD
jest on pełen gotch i ograniczeń.Wypróbuj: NSRecursiveLock
źródło
Rysunek: Opublikuję moją implementację Swift 5, opartą na wcześniejszych odpowiedziach. Dzięki chłopaki! Uważam, że warto mieć taką, która zwraca również wartość, więc mam dwie metody.
Oto prosta klasa do zrobienia jako pierwsza:
Następnie użyj go tak, jeśli potrzebujesz wartości zwracanej:
Lub:
źródło
public class func synced<T>(_ lock: Any, closure: () -> T)
, działa zarówno na void, jak i na inne typy. Są też rzeczy odrastające.Detale
xCode 8.3.1, szybki 3.1
Zadanie
Odczytaj wartość zapisu z różnych wątków (asynchronicznie).
Kod
Stosowanie
Pełna próbka
źródło
W przypadku opakowań właściwości Swift używam teraz:
Następnie możesz po prostu zrobić:
lub
Następnie uzyskaj dostęp do zmiennej w normalny sposób.
źródło
DispatchQueue
ukrytego przed użytkownikiem. Znalazłem to odniesienie SO, aby się uspokoić: stackoverflow.com/a/35022486/1060314Podsumowując, tutaj podaj bardziej powszechny sposób, który obejmuje wartość zwracaną lub void i rzucaj
źródło
Dlaczego to utrudnia i kłopot z zamkami? Użyj Barier Wysyłkowych.
Bariera wysyłki tworzy punkt synchronizacji w równoległej kolejce.
Podczas działania żaden inny blok w kolejce nie może działać, nawet jeśli jest równoległy i dostępne są inne rdzenie.
Jeśli to brzmi jak wyłączna blokada (zapis), to właśnie tak. Bloki bez barier można traktować jako współdzielone (czytane) zamki.
Tak długo, jak cały dostęp do zasobu odbywa się przez kolejkę, bariery zapewniają bardzo tanią synchronizację.
źródło
W oparciu o „eurobur” przetestuj przypadek podklasy
Wynik:
źródło
dispatch_barrier_async jest lepszym sposobem, nie blokując bieżącego wątku.
dispatch_barrier_async (accessQueue, {dictionary [object.ID] = object})
źródło
Inną metodą jest utworzenie nadklasy, a następnie jej odziedziczenie. W ten sposób możesz używać GCD bardziej bezpośrednio
źródło