Czasami chcesz zablokować wątek podczas oczekiwania na zdarzenie.Blokowanie i oczekiwanie na zdarzenie
zwykle zrobić to tak:
private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private void OnEvent(object sender, EventArgs e){
_autoResetEvent.Set();
}
// ...
button.Click += OnEvent;
try{
_autoResetEvent.WaitOne();
}
finally{
button.Click -= OnEvent;
}
Wydaje się jednak, że powinno to być coś, co mogłem wydobyć do wspólnej klasy (a może nawet coś, co już istnieje w ramach).
Chciałbym móc zrobić coś takiego:
EventWaiter ew = new EventWaiter(button.Click);
ew.WaitOne();
EventWaiter ew2 = new EventWaiter(form.Closing);
ew2.WaitOne();
Ale nie mogę znaleźć sposób, aby zbudować taką klasę (nie mogę znaleźć dobrą prawidłową drogę do zabicia wydarzenie jako argument). Czy ktoś może pomóc?
Aby dać przykład, dlaczego to może być przydatne, należy rozważyć coś takiego:
var status = ShowStatusForm();
status.ShowInsertUsbStick();
bool cancelled = WaitForUsbStickOrCancel();
if(!cancelled){
status.ShowWritingOnUsbStick();
WriteOnUsbStick();
status.AskUserToRemoveUsbStick();
WaitForUsbStickToBeRemoved();
status.ShowFinished();
}else{
status.ShowCancelled();
}
status.WaitUntilUserPressesDone();
Jest to o wiele bardziej zwięzły i czytelny niż równoważny kod napisany z logiką rozłożone pomiędzy wieloma metodami. Ale aby zaimplementować WaitForUsbStickOrCancel(), WaitForUsbStickToBeRemoved i WaitUntilUserPressesDone() (zakładamy, że otrzymujemy zdarzenie podczas wstawiania lub usuwania pamięci USB), muszę za każdym razem ponownie zaimplementować "EventWaiter". Oczywiście musisz uważać, aby nigdy nie uruchamiać tego w wątku GUI, ale czasami jest to warte kompromisu dla prostszego kodu.
Alternatywą mogłoby wyglądać tak:
var status = ShowStatusForm();
status.ShowInsertUsbStick();
usbHandler.Inserted += OnInserted;
status.Cancel += OnCancel;
//...
void OnInserted(/*..*/){
usbHandler.Inserted -= OnInserted;
status.ShowWritingOnUsbStick();
MethodInvoker mi =() => WriteOnUsbStick();
mi.BeginInvoke(WritingDone, null);
}
void WritingDone(/*..*/){
/* EndInvoke */
status.AskUserToRemoveUsbStick();
usbHandler.Removed += OnRemoved;
}
void OnRemoved(/*..*/){
usbHandler.Removed -= OnRemoved;
status.ShowFinished();
status.Done += OnDone;
}
/* etc */
uważam, że znacznie trudniej odczytać. Prawdą jest, że przepływ nie będzie zawsze taki liniowy, ale kiedy jest, podoba mi się pierwszy styl.
Jest to porównywalne do używania funkcji ShowMessage() i Form.ShowDialog() - blokują one również do momentu pojawienia się "zdarzenia" (chociaż uruchomią pętlę komunikatów, jeśli zostaną wywołane w gui-thread).
Jestem ciekaw dlaczego dokładnie, czego chcesz to zrobić ... możesz rozwinąć? –
Nadal nie rozumiem, dlaczego chcesz zablokować wątek, zamiast tylko czekać na wydarzenie - co to oznacza? –
Czy kiedykolwiek to naprawiłeś? Mam ten sam problem. – Carlo