Ключевое слово события в c# изменяет объявление делегата. Это предотвращает прямое присвоение на делегата (можно только использовать + = и - = на событии), и это предотвращает вызов делегата снаружи класса.
, Таким образом, Вы могли изменить свой код для сходства с этим:
public class CustomGUIElement
{
...
public MouseEventHandler Click;
// etc, and so forth.
...
}
Тогда можно вызвать событие снаружи класса как это.
myCustomGUIElement.Click(sender,args);
недостаток состоит в том, что код с помощью класса может перезаписать любые зарегистрированные обработчики очень легко с кодом как это:
myCustomGUIElement.Click = null;
, который не позволяется, если делегат Щелчка объявляется как событие.
Посмотрите на ExecutorCompletionService . Он делает все, что вы хотите.
void solve(Executor e, Collection<Callable<Result>> solvers)
throws InterruptedException, ExecutionException {
//This class will hold and execute your tasks
CompletionService<Result> ecs
= new ExecutorCompletionService<Result>(e);
//Submit (start) all the tasks asynchronously
for (Callable<Result> s : solvers)
ecs.submit(s);
//Retrieve completed task results and use them
int n = solvers.size();
for (int i = 0; i < n; ++i) {
Result r = ecs.take().get();
if (r != null)
use(r);
}
}
Преимущество использования CompletionService заключается в том, что он всегда возвращает первый завершенный результат. Это гарантирует, что вы не дожидаетесь завершения задач, и позволяет незавершенным задачам работать в фоновом режиме.
Я бы порекомендовал посмотреть на Java Executors .
Вы отправляете несколько задач и для каждой из них получаете объект Future . Ваша работа обрабатывается в фоновом режиме, и вы перебираете объекты Future (как в приведенном выше примере). Каждое будущее возвращает результат, когда он становится доступным (путем вызова get ()
- это блокируется, пока результат не будет сгенерирован в отдельном потоке)
Самое близкое, что я могу придумать, - это использовать CompletionService
для накопления результатов по мере их завершения.
Простой пример:
ExecutorService executor = Executors.newSingleThreadExecutor(); // Create vanilla executor service.
CompletionService<Result> completionService = new ExecutorCompletionService<Result>(executor); // Completion service wraps executor and is notified of results as they complete.
Callable<Result> callable = new MyCallable();
executor.submit(callable); // Do not store handle to Future here but rather obtain from CompletionService when we *know* the result is complete.
Future<Result> fut = completionService.take(); // Will block until a completed result is available.
Result result = fut.get(); // Will not block as we know this future represents a completed result.
Я бы не рекомендовал упаковывать это за интерфейсом Iterator
, поскольку метод Future
get ()
может выдавать два возможных отмеченных исключения: ExecutionException
и InterruptedException
, и поэтому вам нужно будет поймать и либо проглотить их, либо повторно выбросить как RuntimeException
, ни то, ни другое не очень хорошо. Кроме того, ваши методы Iterator
hasNext ()
или next ()
потенциально должны блокироваться, если выполнялась задача, что может показаться нелогичным для клиентов, использующих итератор
. Вместо этого я бы реализовал свой собственный более наглядный интерфейс; например
public interface BlockingResultSet {
/**
* Returns next result when it is ready, blocking is required.
* Returns null if no more results are available.
*/
Result take() throws InterruptedException, ExecutionException;
}
(методы, называемые take ()
, обычно представляют собой блокирующий вызов в пакете java.util.concurrent
).