Czy jest jakieś łatwiejsze rozwiązanie podczas przenoszenia zdarzenia resetowania ręcznego okna do pthread, niż zmienna warunkowa pthread + pthread mutex + flaga, jeśli zdarzenie jest ustawione lub rozbrojone?Zdarzenie resetowania ręcznego okna pthread
Odpowiedz
Pthreads to konstrukcje niskopoziomowe. Nie, nie ma prostszego mechanizmu; pthread_cond__*
jest koncepcyjnie podobny do zdarzenia automatycznego resetowania. Bądź ostrożny, pthread_cond_wait
może mieć fałszywe wybudzenia, więc nigdy nie powinien być używany bez jakiejś zewnętrznej flagi niezależnie od sytuacji.
Jednak tworzenie własnych nie byłoby zbyt trudne.
#include <pthread.h>
#include <stdbool.h>
struct mrevent {
pthread_mutex_t mutex;
pthread_cond_t cond;
bool triggered;
};
void mrevent_init(struct mrevent *ev) {
pthread_mutex_init(&ev->mutex, 0);
pthread_cond_init(&ev->cond, 0);
ev->triggered = false;
}
void mrevent_trigger(struct mrevent *ev) {
pthread_mutex_lock(&ev->mutex);
ev->triggered = true;
pthread_cond_signal(&ev->cond);
pthread_mutex_unlock(&ev->mutex);
}
void mrevent_reset(struct mrevent *ev) {
pthread_mutex_lock(&ev->mutex);
ev->triggered = false;
pthread_mutex_unlock(&ev->mutex);
}
void mrevent_wait(struct mrevent *ev) {
pthread_mutex_lock(&ev->mutex);
while (!ev->triggered)
pthread_cond_wait(&ev->cond, &ev->mutex);
pthread_mutex_unlock(&ev->mutex);
}
To może nie zmieścić się na zużycie, jak często będziesz mieć inny zamek, który chcesz chcesz użyć zamiast ev->mutex
, ale to jest sedno tego, jak to jest zwykle używany.
Wydaje mi się, że wydarzenia w Windows są bardziej zbliżone do semaforów. To znaczy. do automatycznego resetowania używałbyś semafora binarnego i funkcji sem_timedwait().
No nie ma żadnych łatwiejsze rozwiązanie ale następujący kod rade:
void LinuxEvent::wait()
{
pthread_mutex_lock(&mutex);
int signalValue = signalCounter;
while (!signaled && signalValue == signalCounter)
{
pthread_cond_wait(&condition, &mutex);
}
pthread_mutex_unlock(&mutex);
}
void LinuxEvent::signal()
{
pthread_mutex_lock(&mutex);
signaled = true;
signalCounter++;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&mutex);
}
void LinuxEvent::reset()
{
pthread_mutex_lock(&mutex);
signaled = false;
pthread_mutex_unlock(&mutex);
}
Dzwoniąc signal(), zdarzenie przechodzi w stanie sygnalizacji i wszyscy czekają wątek zostanie uruchomiony. Wtedy zdarzenie pozostanie w stanie sygnalizowanym i wszystkie wywołania wątku wait() nie będą czekać. Wywołanie funkcji reset() spowoduje powrót zdarzenia do stanu bez sygnalizacji.
Parametr signalCounter znajduje się na wypadek, gdy wykonasz szybki sygnał/reset, aby obudzić wątki oczekujące.
można łatwo wdrożyć wydarzenia ręcznego resetu z rur:
wydarzenie jest w stanie wyzwolenia -> jest coś odczytać z rury
SetEvent -> write()
ResetEvent - > read()
WaitForMultipleObjects -> poll() (lub wybierz()) do czytania
"SetEvent" operacja powinna napisać coś g (np. 1 bajt dowolnej wartości) tylko po to, aby umieścić rurę w stanie niepustym, więc kolejna operacja "Czekaj", czyli poll() dla danych dostępnych do odczytu, nie zostanie zablokowana.
Operacja "ResetEvent" odczyta zapisane dane, aby upewnić się, że rura jest ponownie pusta. Koniec odczytywania potoku powinien być wykonany bez blokowania, dlatego próba zresetowania (odczytania) wcześniej zresetowanego zdarzenia (pusta rura) nie będzie blokować - fcntl (pipe_out, F_SETFL, O_NONBLOCK) Ponieważ może być więcej niż 1 SetEvents przed ResetEvent, należy zakodować go tak, że czyta tyle bajtów, ile jest w rurze:
char buf[256]; // 256 is arbitrary
while(read(pipe_out, buf, sizeof(buf)) == sizeof(buf));
Zauważ, że czeka na razie nie czyta z rury i stąd „wydarzenie” pozostanie w stan wyzwolony do czasu operacji resetowania.
Preferuję podejście rurowe, ponieważ często nie trzeba po prostu zdarzenia, na które trzeba czekać, ale wiele obiektów, np. WaitForMultipleObjects(...)
.Oraz z wykorzystaniem rur można łatwo wymienić okna WaitForMultipleObjects
połączenia z poll(...)
, select
, pselect
i epoll
.
było sposób lekkie do synchronizacji procesu zwanego Futex
(szybkie połączenie systemu Strefa blokowania). Była funkcja futex_fd
, aby uzyskać jeden lub więcej deskryptorów plików dla futexes. Ten deskryptor pliku, wraz z możliwie wielu innych reprezentujących prawdziwych plików, urządzeń, gniazd lub podobne mogłyby przejdzie do select
, poll
lub epoll
. Niestety został usunięty z jądra. Więc sztuczki rury pozostają jedynym obiektem, aby to zrobić:
int pipefd[2];
char buf[256]; // 256 is arbitrary
int r = pipe2(pipefd, O_NONBLOCK);
void setEvent()
{
write(pipefd[1], &buf, 1);
}
void resetEvent() { while(read(pipefd[0], &buf, sizeof(buf)) > 0) {;} }
void waitForEvent(int timeoutMS)
{
struct pollfd fds[1];
fds[0].fd = pipefd[0];
fds[0].events = POLLRDNORM;
poll(fds, 1, timeoutMS);
}
// finalize:
close(pipefd[0]);
close(pipefd[1]);
+1 Chciałbym zagłosować na 10! –
Szukaliśmy podobnego rozwiązania portu niektóre mocno wielowątkowy kodu C++ z Windows na Linuksa, a skończyło się na pisanie open source, MIT-licencjonowany Win32 Events for Linux library. Powinno to być rozwiązanie, którego szukasz, i zostało dokładnie sprawdzone pod względem wydajności i zużycia zasobów.
Realizuje zdarzenia ręczne i automatyczne resetowanie, a także funkcje WaitForSingleObject
i WaitForMultipleObject
.
To wygląda bardzo ładnie. Jest zbyt zły * deskryptory nix, semafory, zdarzenia, wątki, gniazda, itp. Nie są dostępne w zunifikowany sposób, tak jak w Windowsie, gdzie jedno połączenie reguluje je wszystkie. – jww
My (pełne ujawnienie: Pracuję na NeoSmart Technologies) napisał open source (MIT licencjonowany) Biblioteka zwana pevents która implements WIN32 manual and auto-reset events na POSIX, i obejmuje zarówno WaitForSingleObject i WaitForMultipleObjects klonów. Od tamtej pory jest to jakaś adopcja (jest używana w Steam na Linuksie/Macu) i działa całkiem nieźle.
Chociaż osobiście zaleciłbym stosowanie wielowątkowych i sygnalizacyjnych paradygmatów POSIX podczas kodowania na urządzeniach POSIX, peventy dają ci inny wybór, jeśli tego potrzebujesz.
Nie zapomnij, że zdarzenie szyby auto resetu będzie „pamiętać”, że to zostało zasygnalizowane i informuje kolejny wątek, który czeka, a następnie zresetować się. pthread_cond_signal może faktycznie nic nie robić, jeśli nie ma wątków oczekujących, więc "zdarzenie" w tym przypadku wydaje się nie mieć miejsca. – ScaryAardvark
+1 Dziękuję bardzo! – ceztko
@ephemient „Na pthread_cond_wait() i pthread_cond_timedwait() funkcje są wykorzystywane do blokowania na zmiennej warunkowej Nazywane są z mutex zablokowanej przez wywołującego wątku lub niezdefiniowanej zachowanie spowoduje”. Czy nie powinieneś przechwycić muteksa przed każdym wywołaniem pthread_cond_wait? Te funkcje atomowe powodują uwolnienie muteksu i nić telefonicznej zablokowania Cond zmiennej warunek; – user877329